Find the last block
Permalink 1 user found helpful
I need to set a class of "last" to the div containing the last Block in my Area.
Is there something built into C5 that tells me the current count of the blocks being setup? Or how can I figure out when the last block is being added and add this class into the Div I have setup in the block?
Is there something built into C5 that tells me the current count of the blocks being setup? Or how can I figure out when the last block is being added and add this class into the Div I have setup in the block?
Thanks for the help and suggestions! I agree that your 3rd choice would work the best for me...
I had actually already tried that, but my jquery script wasn't returning any of the dynamically created blocks that where created by C5.
I'm guessing it's probably an issue with the position of my javascript include lines... is there a good resource for C5 on best practices for including jquery files and css files in a theme?
I had actually already tried that, but my jquery script wasn't returning any of the dynamically created blocks that where created by C5.
I'm guessing it's probably an issue with the position of my javascript include lines... is there a good resource for C5 on best practices for including jquery files and css files in a theme?
well, you can already get an array of all blocks in an area, then you can do a foreach and apply the class, look at the autonav block as it applies a </li></ul> to the end
Mnkras, that's kind of like my option #2 above -- but I think he's talking about applying this as part of his theme, so it works across all blocks (regardless of type), in which case it would be impractical to create custom templates for all of them.
several ways to solve this i think, one could be to create a 'last' template for blocks we assume to appear sometimes at the bottom of an area
another might be to do it with the design stuff for blocks.
$a = new Area('Area Name'); $c = Page::getCurrentPage(); $areaObjects = $a->getAreaBlocksArray($c); $lastAreaBlock = array_shift(array_reverse($areaObjects)); $lastAreaBlock->setCustomTemplate('last'); $a->display($c);
another might be to do it with the design stuff for blocks.
Ok, this worked perfectly with the addition of one line of code... I needed to reset all of the other blocks back to the original view before setting the last block to the 'last' view. Here's my code:
$a = new Area('Tour Dates'); $c = Page::getCurrentPage(); $areaObjects = $a->getAreaBlocksArray($c); $lastAreaBlock = array_shift(array_reverse($areaObjects)); foreach($areaObjects as $bl) { $bl->setCustomTemplate('view.php'); } $lastAreaBlock->setCustomTemplate('templates/last.php'); $a->display($c);
Ok, I've figured out the solution (at least good enough for this project) using the advice from jordanlev (opt. 2 above) and Mnkras.
Here is what I put in my template (I have added notes for anyone who wants to try and duplicate for their own needs).
Here is what I put in my template (I have added notes for anyone who wants to try and duplicate for their own needs).
<?php // Create a counter variable $count = 1; // Create the area $a = new Area('Tour Dates'); // Create an array of the blocks $blocks = $a->getAreaBlocksArray($c); // Store the size of the array $numBlocks = sizeof($blocks); // Loop through the blocks foreach($blocks as $ni) { // If this is the last block, add a div with a class of "last" if ($count==$numBlocks) { echo "<div id=\"dateBlock\" class=\"last\">"; // Else, add a div
Viewing 15 lines of 26 lines. View entire code block.
Be aware that if the area has any custom designs or if it has a layout, those won't get displayed because you aren't calling $a->display($c).
But if you don't care about those things for this situation, then this is a great solution.
But if you don't care about those things for this situation, then this is a great solution.
Ahh, good point... I guess my solution doesn't work for me.
Joe,
Did you put your code inside the $(document).ready(function() { ... }); construct? That's where you want to put things that will run after all of the page elements are loaded, and it doesn't matter where in your code you put this -- it will magically just work.
If you want to paste the javascript you used I'd be happy to take a look at it.
Did you put your code inside the $(document).ready(function() { ... }); construct? That's where you want to put things that will run after all of the page elements are loaded, and it doesn't matter where in your code you put this -- it will magically just work.
If you want to paste the javascript you used I'd be happy to take a look at it.
Jordan - I'm using my own .js files so I've been adding them to the header like this:
and then adding:
Below it...
I'm very new to concrete5, so I'm kind of going through a quick crash course, trying to figure out the best way to do things.
But, is there a better way to include my own external .js files, or is it best to add my scripts into an already created concrete5 script? And if so, where is that script to add to?
Most of my jquery has broke since trying to implement it into concrete5, so I do need to figure out the proper way to implement it with concrete5.
Thanks for the help!
<script type="text/javascript" src="<?=$this->getThemePath()?>/js/bannerRotator.js"></script>
and then adding:
<?php Loader::element('header_required'); ?>
Below it...
I'm very new to concrete5, so I'm kind of going through a quick crash course, trying to figure out the best way to do things.
But, is there a better way to include my own external .js files, or is it best to add my scripts into an already created concrete5 script? And if so, where is that script to add to?
Most of my jquery has broke since trying to implement it into concrete5, so I do need to figure out the proper way to implement it with concrete5.
Thanks for the help!
Joe,
What's inside your own .js files? If it's just code that's not wrapped in the jquery $(document).ready(function() { ... }); thing, then you're going to have problems with it running at the wrong time regardless of how it's included.
As for how to include the files, yes, the way you're doing it is the best way. One problem you may be having is if you're including the jquery library yourself -- Concrete5 already includes that automatically on every page so if you include it also, it might cause problems.
What's inside your own .js files? If it's just code that's not wrapped in the jquery $(document).ready(function() { ... }); thing, then you're going to have problems with it running at the wrong time regardless of how it's included.
As for how to include the files, yes, the way you're doing it is the best way. One problem you may be having is if you're including the jquery library yourself -- Concrete5 already includes that automatically on every page so if you include it also, it might cause problems.
Jordan - Yes, I am using $(document).ready(function() { ... }); - with my code located inside.
I removed my jquery library include, but when I run my site I get this error in my console:
Uncaught ReferenceError: $ is not defined
I removed my jquery library include, but when I run my site I get this error in my console:
Uncaught ReferenceError: $ is not defined
Hmm... make sure your own javascript file includes (e.g. <script type="text/javascript" src="<?=$this->getThemePath()?>/js/bannerRotator.js"></script>) are AFTER the concrete5 ones (<?php Loader::element('header_required'); ?>)
Ahh... Thanks! In trying to figure all this out I've been reading conflicting advice on these things. I read somewhere to put the includes before the "header_required".
Everything seems to work now with my includes coming after the "Loader::element('header_required');" and not including my own jquery.js file.
Thanks again for the help! On to my next issue and forum topic...
Everything seems to work now with my includes coming after the "Loader::element('header_required');" and not including my own jquery.js file.
Thanks again for the help! On to my next issue and forum topic...
Here's how I implemented option #2 for a project I'm working on. I had rows of a custom block type (created with Designer Content), and needed HR tags after all but the last in an area. I was working from pre-sliced HTML... if it had been my slice, I would probably have styled using :first-child (and still might re-work it, but wanted to try this first).
This code is run in the view.php every time a block of this type is displayed. I'm assuming the Area and Block objects are cached and not pulled from the database every time, but not sure.
This code is run in the view.php every time a block of this type is displayed. I'm assuming the Area and Block objects are cached and not pulled from the database every time, but not sure.
1) Hack the concrete/models/area.php file, in the "foreach ($blocksToDisplay as $b) {" loop in the display() function -- get a count of that $blocksToDisplay array before the loop starts, increment a variable every time through the loop, and at the end, check if it's at the max, and output the enclosing div with its class (and remember to set the closing </div> after the block has been outputted). Note that this method is not recommended unless you really need this functionality because hacking the core code can lead to problems, and means you have to re-apply it if you ever update the system. Also, this method has the disadvantage of applying to ALL areas outputted on your site (although perhaps you could figure out how to determine which area is being outputted and decide whether or not to output the div based on that somehow?)
2) Make a custom template for the block that somehow calls the surrounding area and gets the total block count, and its position in the area, and sees if it's the last one or not. Output the div accordingly. This only works if you're talking about a single type of block though, as you can't do this for every single block in the system (well, you could I guess, but seems like a lot of work).
3) In your page type template, use $a->setBlockWrapperStart('<div class="myBlock">') and $a->setBlockWrapperEnd('</div>') (before $a->display()) and then use jquery to check for the last element inside the divs having the "myBlock" class, and rewrite the dom on that last block to give its div the "last" class. As long as you are okay having this not work when javascript is unavailable, this is definitely your best bet.