Creating WordPress Themes: Split Navigation with CSS

So, we’ve covered the basics at this point, an the usefulness of the query_posts() function, as well as how to do cool things with your front page. I hinted at a few tutorials to come, so let’s start with one of those – it’s as good a place as any!

What we’re going to cover today is one of the most commonly requested functions I see – having a main navigation (usually at the top of the page) and have the children of whatever page you’re on appear in the sidebar.

Now there’s an excellent plugin out there (works for both categories and Pages) Called “Fold Page List” – brought to you by the fine folks at Web Space Works. I’ve used these plugins a few times, and they’re pretty handy – however they don’t quite pull off what I’m talking about here. What these plugins do is provide a list of Pages and Categories in your sidebar, and whichever section you’re on, the sublist is folded out (while the others are closed) – this is close to what I’m talking about, but no cigar.

It’ll help a lot of you also take a look at an old tutorial I have here on the site: Dynamic Stylesheets. It’s not necessary, but it sure does make things a whole hell of a lot easier.
So let’s get to it. This one’s a bit complicated, so I’ll try and take it slow. :)

So, initially, what we’re going to do is write a function that grabs the current page we are on (be it a category or Page) – which really isn’t that hard. The difficult part is getting the the ultimate top-level Page or category – this is the part that people tend to have the most difficulty with, because WordPress doesn’t easily allow you to get the grandparent of a page or category. Parent? easy. Grandparent or great-grandparent is a little harder. But lucky you, we’ve already covered this bit of code. (It definitely comes in handy, and is useful in several applications!) You’ll notice that the output of that code will use the Page toplevel parent of the Page or Category you are on, and also supply a class that determines if it’s a Page or a category section. Before, we only used it so we could add to the <body> tag, and use it for helpful dynamic styling (over using multiple template files). Now we’re going to take it just a step further.

Since the previous bit of code was specifically set to determine the body ID and class, we’re going to have to edit it just slightly to make it more usable. I’ll start by just giving you the code section, and simply explaining the changes I’ve made – it’ll be easier ;)

<?php
function toplevel($type) {
global $post;
if(is_home() || is_front_page()) { // we're on the index page
$name = 'home';
$class = '';

} else if(is_page()) { // if we're on a static Page
$isparent = $post->post_parent; // test to see if this it a parent
$postID = $post->ID; // get this post's ID
$ancestors = get_post_ancestors($post);
$top = end($ancestors);
if($isparent == '0') { // if there is no parent, grab this page's name
$getname = get_post($postID);
} else {
$getname = get_post($top);
}
$name = $getname->post_name;
$class = 'page';

} else if(is_category()) { // if we're on a category listing
$cat = get_query_var('cat');
$parents = get_category_parents($cat,FALSE,'^',TRUE);
$get_parent = explode('^',$parents);
$name = $get_parent[0];
$class = 'category';

} else if(is_single()) { // if this is a single post page
$name = $post->post_name;
$class = 'single';
}

if($type == "name") {
return $name;
} else if ($type == "class") {
return $class;
}
} ?>

You’ll be happy to know that it’s been reported (I haven’t confirmed it yet, but I soon will) that WordPress 2.8 makes this function obsolete with the use of a new tag: body_class() – yay for integrating this into core! – but the rest of the tutorial should, indeed, be made that much easier for that addition.
Okay, so the differences here are pretty easy to point out. In the original code I posted last time, we defined the classes and ID’s thoroughly by even going as far as adding in the id="" to the function above. but since we now want to use this stuff elsewhere as well, we need to only grab the names and classes – and we’ll do the rest through the template files themselves. So before, when we would add to the body tag like so:

<body<?php toplevel(); ?>>

we need to be a little more specific. You’ll also note that we can specify if we even want a class added or not, and what will appear by adding the variables of $name and $class. And since we are now returning the values (so we can use them later on), we have to tell it when to actually echo these values out. So now, for your body tag, it’ll look more like this:

<body id="<?php echo toplevel('name'); ?>" class="<?php echo toplevel('class'); ?>">

It is a little more code, but it’s definitely more customizable.

Now that all of that’s out of the way, let’s move on to what we actually do with this code to pull off the desired effect. So now that we have our body class and ID set, and we know that the base for Pages and categories will be using the main/most toplevel parent, we can start the fun. Many of my clients actually use a combination of wp_list_pages() and wp_list_categories() for their main navigation, mostly because they like to have a section for either an updated news/articles section, or for newsletters – and this is what the “blog” part is based on. So once you figure out how you want your main navigation to be ordered like, you may then proceed to do the fun “split” part in the sidebar.

I know sometimes I can be “too wordy” and I’m not quite clear on what I’m saying – but if you want to see an example of what I’m talking about, check out the client site I applied this to: EnergyLink.
Basically, what you then need to do is write a query that takes the ID of what Page or category you are on, test to see if you’re already on a parent, child or grandchild, and then display the children of the section you are in. All the while, you use CSS to determine the toplevel name and highlight that button in your main navigational menu. So here’s the function you actually need to write to list the proper stuff in the sidebar. (It’s actually based off the code provided in the WordPress codex, regarding showing child Pages. Just a little tweaking here and there, and you’re good to go.)

<?php function bb_list_children() {
global $post;
if(is_page()) {
if($post->post_parent)
$children = wp_list_pages("title_li=&child_of=".$post->post_parent."&echo=0");
else
$children = wp_list_pages("title_li=&child_of=".$post->ID."&echo=0");
} else if(is_category()) {
$catid = get_query_var('cat');
$cat = &get_category($catid);
$parent = get_category_parents($catid, FALSE, ',');
$catlist = explode(',',$parent);
$toplevelname = $catlist[0]; //top level nicename
$id = get_cat_id($toplevelname);
$getcat = get_categories('child_of='.$id);
if(empty($getcat)) { } else {
$children = wp_list_categories("title_li=&child_of=".$id."&echo=0&hierarchical=0&hide_empty=1");
}

}

if ($children) {
echo '<ul id="children">';
echo $children;
echo '</ul>';
}
} ?>

Now, simply go to your sidebar.php file, insert <?php bb_list_children(); ?> where you would like the “child” list appear, and you’re set. No matter what Page or category you are on, it’ll list the children (if any) of that Page or category, and the body tag will house the most top level Page or Category slug as the ID of that page. You may then use your stylesheet to define the appearance of the main navigational menu so that it’s highlighted when you’re in any part of that section.

There is another way to pull this off, and it’s a little bit easier, but less dynamic. I’ll save that for another tutorial, because this one’s long enough. Believe me, there’s plenty of tutorials to go around without putting them all in one post ;) I do hope this helped you out!

Up next: Using Custom Fields

Comments

  • Matt

    Shelly,

    This is exactly what I was looking for. I am not a full-time programmer but I understand your descriptive teachings for some reason. I hit one small problem, I am using the Multi-level navigation plugin and when I inserted the scripts here it whacked out my menu (however the sub nav appears!) I wouldn’t know where to begin to find a fix. I need the drop-down on the main nav, and the sub page nav. Use something other than the Multi-level Nav? I chose it for being so dynamic as well since my client will be adding pages in the future. You can see the dev site at the link provided. Thanks! Matt

    June 15, 2009 at 5:54 pm Reply
    • Shelly

      Hi Matt,

      yes, this won’t work – not right off the bat anyway – with the Multi-level navigation plugin. The Multi-nav plugin is based off of Son of Suckerfish. The plugin just makes it easy to add it to your WordPress site – however with that ease, you don’t get as much customization, especially since the Multi-nav plugin does certain things for you already. This bit of code assumes you’re doing it from scratch.

      Probably what you need to do is view the source code and check out the classes the multi-nav plugin adds in. I know this code *can* work with the multi-nav (I’ve actually done it before), but it’s been a while, and as I recall I had to actually edit the plugin itself to accept the different parameters I added with the code above.

      I’d tell you how I did it, but unfortunately I just got back from vacation (which is why it took so long for me to reply – sorry about that) and I know the answer is in my archived files. I you drop me an email, I’ll see what I can figure out for you (because I know if I say I’ll look for you, I’ll forget if you don’t send me an email to remind me!)

      June 20, 2009 at 8:55 am Reply
      • Matt

        Hi Shelly,

        Sorry for the slow reply, and thanks for your input. I did away with the multi-nav plugin (too much) for a simple site. Therefore I am still using your function above to include the subnav and it works beautifully. I ran into a problem though. The subnav disappears on pages that show posts. I found another subnav plugin and installed and its doing the same thing. Therefore it falls back on my lack of WP skills. Here is an example..

        Normal page. Looks great
        http://arizonaoliveoil.com/about/

        Page with posts. Other content in sidebar still exists except for subnav.
        http://arizonaoliveoil.com/news/

        Thanks for advance for any advice.

        Matt

        August 11, 2009 at 1:47 pm Reply
      • Shelly

        Now again, apologies in *my* delay – I’ve been quite busy lately. I’m looking at your site, and it looks like the subnav is there. On the “News” page, it shows the “calendar”. Is something else supposed to be there?

        August 18, 2009 at 2:04 pm Reply
  • csleh

    One problem I had was when I pasted code, there were some funny quote marks. So it would be a curly quote instead of a ” character. Once I replaced the few stray ” and ‘ it worked.

    I’ll be using something else as this only shows top grandparent level instead of parent/aunt or even siblings.

    June 24, 2009 at 11:48 pm Reply
    • Shelly

      Yes, I’ve had the “curly quotes” problem appear before. I’m looking into plugins to paste code (nicely) without having WordPress’ default filters come in the picture. That does get annoying.

      As for siblings, etc… that’s not the point of this code, so I *would* assume you’d use something else. If you want that type of display, there’s two plugins listed at the very top of the post which would work for you that do exactly that. but as I stated early on – that’s not what *this* code is for. It’s so that aunts/siblings *won’t* show.

      June 25, 2009 at 7:51 am Reply
  • Have your say: