How to turn your auto-nav block into a JQuery Accordion menu.
Out of the box the auto-nav block in concrete5 renders a nested unordered list <ul>
containing the various levels and sublevels of pages that you specify when you add the block to a page.
Using an unordered list is a pretty standard way of handling website menus because it lends itself well to styling and manipulating with javascript.
First I create the autonav block in my C5 website.
Next we choose the navigation parameters – we want to show all levels from the top down.
Once we’ve set those options, clicked add and then saved the page we should have a nested unordered list with all of the sublevels expanded- something along the lines of this.
As we can see this is completely unstyled and is just using default styles, indentations etc and is fully expanded to show the levels we selected.
So – we have our navigation element and it contains all of our pages – how easy was that? What we want to do though is make an accordion style menu to that the sub-levels slide out when clicked on. We need some javascript to accomplish this.
Luckily Concrete5 is loaded up with JQuery by default so we don’t need to add any code to call it.
I googled ‘Jquery accordion menu’ and chose a short piece of code that does the job. Full kudos goes to Marco at http://www.i-marco.nl/weblog/ for providing this code on his website for all to use:
/*
Simple JQuery menu.
Copyright 2007-2010 by Marco van Hylckama Vlieg
Free to use any way you like.
*/
jQuery.fn.initMenu = function() {
return this.each(function(){
var theMenu = $(this).get(0);
$(‘.acitem’, this).hide();
$(‘li.expand > .acitem’, this).show();
$(‘li.expand > .acitem’, this).prev().addClass(‘active’);
$(‘li a’, this).click(
function(e) {
e.stopImmediatePropagation();
var theElement = $(this).next();
var parent = this.parentNode.parentNode;
if($(parent).hasClass(‘noaccordion’)) {
if(theElement[0] === undefined) {
window.location.href = this.href;
}
$(theElement).slideToggle(‘normal’, function() {
if ($(this).is(‘:visible’)) {
$(this).prev().addClass(‘active’);
}
else {
$(this).prev().removeClass(‘active’);
}
});
return false;
}
else {
if(theElement.hasClass(‘acitem’) && theElement.is(‘:visible’)) {
if($(parent).hasClass(‘collapsible’)) {
$(‘.acitem:visible’, parent).first().slideUp(‘normal’,
function() {
$(this).prev().removeClass(‘active’);
}
);
return false;
}
return false;
}
if(theElement.hasClass(‘acitem’) && !theElement.is(‘:visible’)) {
$(‘.acitem:visible’, parent).first().slideUp(‘normal’, function() {
$(this).prev().removeClass(‘active’);
});
theElement.slideDown(‘normal’, function() {
$(this).prev().addClass(‘active’);
});
return false;
}
}
}
);
});
};
$(document).ready(function() {$(‘.menu’).initMenu();});
So how do we get this to work with our navigation element in Concrete5.
The answer is – very easily.
From the javascript above I can see that the slide out functionality applies when a <UL>
is classed ‘acitem’.
Therefore I need each nested <UL>
element in the Concrete5 auto-nav to have this class.
Concrete5 is built to be easily customisable and also very extensible.
Now I know from investigating the auto-nav block that the code is contained in /public_html/concrete/blocks/autonav.
Investigating the code shows me that the nested menu is generated in /public_html/concrete/blocks/autonav/view.php.
Now here follows one of the clever features of Concrete5 – I do not need to mess around with the core code.
I can create a custom template for my menu: the default auto-nav contains everything I need and I just want to add one line of code. Rather than override the base auto-nav which could have a cascade effect on other blocks that use it I can use it for my custom view.
Example is probably the best way to show how this works so in /public_html/blocks I replicate the autonav folder including the /templates subfolder
I now have:
/public_html/blocks/autonav/templates
and
/public_html/concrete/blocks/autonav/templates
I copy
/public_html/concrete/blocks/autonav/view.php
to
/public_html/blocks/autonav/templates/accordion.php
(*note that I do not need to copy all of the files under /public_html/concrete/blocks/autonav – just the one I am changing).
so I can now make changes to my copy of accordion.php, they will be reflected in the website but the core concrete5 code is unchanged.
The code change in accordion.php is super-simple – just find the following code block:
$thisLevel = $ni->getLevel();
if ($thisLevel > $lastLevel) {
echo(“<ul>”);
and change the last line above to
echo(“<ul class='acitem'>”);
Then save accordion.php
Our Javascript file menu.js also needs a little edit to make everything work properly. We just need to change the last line:
$(document).ready(function() {$(‘.menu’).initMenu();});
to read
$(document).ready(function() {$(‘.nav’).initMenu();});
so that it works with the standard Concrete5 .nav class.
We need to apply our custom template to our auto-nav so we click on the containing block and then select 'Custom Template'.
As we've already placed our new template in the correct directory Concrete5 already knows about it and adds it to the possible custom templates for the auto-nav.
We select our new custom template 'accordion' (which directly relates to the accordion.php file we uploaded).
If all of the files are loaded to the relevant places and the new menu.js (containing the amended javascript) is called into our page then we now have a sliding accordion style navigation menu.
All that remains now is to add styles to your unordered list elements to make your menu look how you want.