Folding menu for WordPress pages using jQuery

jQuery folding menu for WordPress

Let’s talk about a simple technique to create folding or collapsible menus for the WordPress Pages widget. A while ago I was doing some coding for a IT Solutions site and they had a large ‘Services’ page with an introduction and 6 sections. They needed a parent page for the intro and 6 child pages and they didn’t wanted to show all the pages and subpages at once. I thought this would be an excellent job for jQuery and a collapsible or folding menu.

We’ll be creating here a folding menu using only jQuery, no CSS no special markup, and enqueueing the script with the recommended technique for WordPress.  Keep reading to see the solution…
This is what is going to happen after we enable our folding menu when someone clicks on a page that has subpages:

folding

Let’s start by looking at the structure and see which selectors we could use. WordPress assigns a current_page_item class to the current page and a page_item class to any other page. When a page has children, the nesting is ‘.page_item ul‘ and ‘.current_page_item ul‘. We can start writing this:

[code=’js’]
jQuery(document).ready(function(){

jQuery(“.page_item ul”).hide();

jQuery(“.current_page_item ul”).slideDown();

});
[/code]

After the document is ready we hide every instance of children pages, and then, if the current page has children, we slideDown the list. That’s it, but wait, what happens if we click in one of the children? now we’re in trouble, we’re inside the ‘.current_page_item ul‘ nesting so jQuery won’t find it and therefore it won’t slide down the menu. Luckily, WordPress adds another class, which is current_page_ancestor, so we nest it with ul, the list that holds the current page, and now we can trigger the slide down effect.

[code=’js’]
jQuery(document).ready(function(){

jQuery(“.page_item ul”).hide();

jQuery(“.current_page_item ul, .current_page_ancestor ul”).slideDown();

});
[/code]

Enough for the jQuery code. Let’s enqueue it using the proper technique. WordPress has a function named wp_enqueue_script that will enqueue our script and it will test if it is already enqueued. If it depends on a library, like jQuery in this case, it will check to see if it’s already enqueued too.

<

pre>

function ilc_addFolding_init(){
wp_enqueue_script('jquery_folding', '/wp-content/themes/default/js/folding.js', array('jquery'));
}
add_action('init', 'ilc_addFolding_init');

[/code]

The first parameter is a handle name and the second is the path to the script. The third parameter is an array of handles that this script depends on. In this case, we only need jQuery, but for example, your script could depend on jQuery and jQuery UI. So, when do we ask WP to call the enqueue? you should call it from an 'init' action. A wp_head hook is too late to enqueue. A template_redirect could work too but I'd rather stick to the action hook recommended in the reference. That's all, you can now test it. You will see the parent pages visible with their children pages hidden. As soon as you click on the parent pages and the page loads, the children pages list will be shown.

Now, one more thing before we end. In the third caption there's a Page 4 > Page 4.2 title, the parent page and the child page. We can easily display the parent using this code within the loop instead of the typical the_title:

<

pre>

post_parent ) {
$page = get_page($post->post_parent);
echo get_the_title($page->ID);
echo ' > ';
} else {
// This is not a subpage
}
the_title(); ?>

[/code]

If this is a page, we grab the post_parent. Then we get the page, and from that page, we retrieve the_title. We render it, add a separator '>' or '' could work, and we end the conditional. Now we can display the current page title using the_title as usual.

Final thoughts

This collapsible menu is in no way perfect. Both jQuery script and parent page title display will only work for one level of nesting. Most of the times this will be enough but you can always extend the script and the php code to display more levels of nesting, to animate the children pages when they are displayed, for example, with fadeIn, or move them from right to left. jQuery always delivers a bit more.

UPDATE: March 11, 2009

Thanks to Kretzschmar bug reports I've modified the script so it will work on all levels of a Pages widget hierarchy. Download the WordPress plugin in the ILC Folding Menu page.

23 thoughts on “Folding menu for WordPress pages using jQuery”

  1. Now I know why my wp_enqueue_script doesn’t work, I was using wp_head as the hook. I changed it to init and it’s working.
    Thanks for sharing this, I was looking for something completely unrelated to this post but thanks to it now I’ve the answer.

    1. This will work for all levels:
      jQuery(document).ready(function(){ jQuery(".page_item ul").hide(); jQuery(".current_page_item ul:first, .current_page_ancestor ul").slideDown(); });
      in fact, I think I will be coding a quick plugin. I will let you know when it’s posted.

  2. Thanks Elliot. This works better but still doesn’t work 100%.

    For example:
    1. page
    1.1 subpage1
    1.1.1 subsubpage 1
    1.1.2 subsubpage 2
    1.2 subpage2

    Clicking on subpage2 opens all subsubpages of subpage1 too.

    1. Ok, I think it’s done now. Check the post, I’ve updated it with the plugin download. The code is:

      jQuery(document).ready(function(){ jQuery(".page_item ul").hide(); jQuery(".current_page_item").parents("ul, li") .map(function () { jQuery(this).slideDown(); }); jQuery(".current_page_item ul:first").slideDown(); });

      Download the WordPress pages folding plugin.

  3. Nice work sir!

    Question – could it remember it’s state – so that if you already have children visible, and you click another child from that branch, the menu doesn’t collapse and open again needlessly?

    Cheers.

  4. I haven’t worked on this since I have last published them. It could be, but I really don’t have time right now 😛 I remember there was a guy that created another plugin based on this one and wrote me an email, you can see the pingback here at the updated plugin at the bottom of the comment list.

  5. Thanks for this article.
    I’ve used it to hide my subcategories in a menu using the Shopp e-commerce plugin.
    As my usual plugin wasn’t working (Shopp don’t use the normal WP categories, posts or pages but has it’s own menu), I’ve customized your code and it’s working great 🙂
    Thanks one more time.

  6. A really great and useful plugin. However I have trouble on my site because there are two menus and it unfolds both. The top menu is a dropdown and should remain that way so only the parent is showing. In the sidebar I have a widget with my pages and I want that to fold/unfold. It works well. Except that when the plugin is activated it unfolds the top menu too. How do I change that?

    1. Annedorte, you’re going to have to add more specificity by including, for example, the ID of the sidebar where the widget is loaded or the ID of the widget, here:
      jQuery(".page_item ul").hide(); jQuery(".current_page_item ul").slideDown();
      For instance, if the widget is loaded in a sidebar with id=”secondary” add this:
      jQuery("#secondary .page_item ul").hide(); jQuery("#secondary .current_page_item ul").slideDown();
      Otherwise you need to enter the ID of the widget, so that it works more specifically, for instance:
      jQuery("#pages-2 .page_item ul").hide(); jQuery("#pages-2 .current_page_item ul").slideDown();
      BTW, make sure you’re using the updated version of this code (which is actually a WordPress plugin) located in this page.

  7. Dear Elio,

    Thank you for your quick reply. I will try that.

    Happy new year.
    Kind regards,
    Annedorte

  8. Instead of the Pages widget, we use Custom Menus (WP 3) which uses classes like menu-item, menu-item-type-TYPE, menu-item-ID, current-menu-item, current-menu-ancestor. Is there a way to modify this plugin/script to work with custom menus instead of a Pages widget?

Leave a Reply