Usable Category Navigation in WordPress Pages

Wordpress isn’t really designed to be used as a CMS. It’s designed for blogging – nonetheless, it’s a surprisingly powerful little content management system, and with a little bit of tweaking you can pretty easily turn it into a nicely flexible system.

One of the challenges that needs to be dealt with is the use of Pages. A “Page,” in WordPress parlance, is a document which sits outside the blog chronology. For the purpose of a CMS, you don’t really want all of your posts to be chronologically organized. You could rewrite the Post templates and the permalink format to eliminate all date information, which would provide you with a nice CMS-like organization – but would also eliminate the blog function. Realistically, you probably want both.

It’s very easy to create a large number of WordPress pages and sort them into a hierarchy. For presentation, the default WordPress function wp_list_pages creates a very nice nested group of unordered lists. This can be styled using CSS to create either a long list of pages with inset sub pages, or a fancy CSS-driven set of drop down or flyout menus.

So far, so good. If you’ve got 10 pages, a single list is fine. But for larger sites, you’re running into other challenges. A straight list of your 150 pages is ugly, difficult to use, and difficult to navigate.

But the classic drop down or flyout menu has some usability issues. For people with mobility impairments, for example, the ability to use a mouse may be limited – and it takes a fair amount of core source hacking in order to attach the javascript needed to make these menus completely keyboard navigable. It’s easy to make certain that the top level link in each category is usable from the keyboard – so your responsibility is to make certain that the top level category link will provide access to all sub pages.

Again, there’s an easy solution: write links to all your further pages into your document content.

Ick. This solution sucks. First of all, if you’re creating this site for a client, you can’t necessarily rely on them to retain the content you’ve carefully created. Second, it’s very awkward to have to hand-maintain links to every page on the site. Why are you using a CMS if you’re going to have to do this anyhow?

Thankfully, although it’s not immediately obvious, WordPress does provide a way to access the children of pages programmatically. Using this code, you can simply create a secondary navigation section which provides easily keyboard-navigable links to all pages below those top level documents.

The Code

post->ID, 'category', true); ?>

post_parent) != the_title('' ,'',false)) { echo "
    "; wp_list_pages("child_of=".$post->post_parent."&title_li="); echo "
"; } if(wp_list_pages("child_of=".$post->ID."&echo=0")) { echo "
    "; wp_list_pages("title_li=&child_of=".$post->ID."&sort_column=menu_order"); echo "
;"; } ?>

What’s going on here?

First up has really nothing to do with the navigation menu itself. This is a header for the category navigation menu, arbitrarily placed in a level 3 heading element. It’s a little oddly phrased – this is because WordPress doesn’t provide access to Category labels for Pages the same way they do for Posts. Therefore, this is actually sourcing a Custom Field – a feature available in WordPress which gives you the ability to attahc any custom information to a document. In this case, I’ve created a custom field named “category” which contains the phrase I wish to be considered the category for this item. Generally, it’s the link text of the top level link, although you could set it to be anything you wish.

We’re trying to talk about usability, however, so I’m inclined to suggest sticking to something appropriate.

Note that the code refers to the variables $post and $post_parent. It’s important to know that “Page” is a formality which indicates a post which resides outside the chronology – from a database management perspective, everything is a post.

The second block of code is a tricky bit. This if query is checking to see whether the current page is a subpage of any other page. In general, WordPress has great built-in conditional functions – you can very easily check whether something is a page (is_page() or whether it’s a category page (is_category(). There isn’t, however, an is_subpage() condition. So, this is the workaround – checking whether the current page is not it’s own parent. In WordPress, pages at the top level of the hierarchy are their own parents. (Let’s not get into genetics, here…I’m concerned.)

Next we’ll use the normal wp_list_pages() function. Again, we’re using the information about the post’s parent in order to determine what pages to list, using the child_of argument to retrieve only the Pages which are children of the current page.

Whoa, there! I see a problem!

Yep, you sure do. At this point, we’re only retrieving child navigation if we’re not on the top level page itself. Well, that’s great. You can get between child pages if you can find your way there!

So that second block of code comes into play. Same idea, except this time we’re fetching children of the current page, without checking whether the page is top level. If it’s got children, great – we’ll display ‘em. If it doesn’t – also great – we won’t.

This sytem does also work for multiple hierarchy levels, although it’s a bit awkward. Here’s what you’ll get with three levels in the hierarchy:

  • Top level page, no children: displays category heading, no children. (The category heading could be removed using another conditional statement; I just haven’t done it.)
  • Top level page, with children: displays all descendants (children and grandchildren.) Grandchildren will be in a nested unordered list inside the list item for their parent.
  • Second level page, no children: displays all sibling pages (pages with the same parent).
  • Second level page, with children: displays all sibling pages AND displays the children of the current page, in a nested unordered list inside the list item for that page.
  • Third level page, no children: display all sibling pages at the grandchild level.

And so on. This code functions in WordPress versions 2.01 and above.

28 Comments to “Usable Category Navigation in WordPress Pages”

  1. Yes – what you’re talking about would probably be best handled with tags. What you want to do isn’t truly hierarchical, since everything in the database would have at least one characteristic within each group. This isn’t really something that WordPress natively handles, although you could probably make something work using tags.

  2. So, your method works best for a static Pages hierarchy? Do you know if there would be a way to use this for something where you have multiple ways of drilling down? For example, a bird guide. You might want to click on colors, shapes, sizes, regions first, and then when you are on the “colors” page click on one of the other three and so on?

    Colors
    Shapes
    Sizes
    Regions

    or

    Shapes
    Regions
    Sizes
    Colors

    You’d quickly get too many different possibilities to set up as a hard categories.

    Thanks, Ian

  3. Hi. I am hoping to use the Pixeled theme for a site. If you look at the nav on the demo, you’ll see it includes fly-overs that show child posts. Would I be able to use your code to do the same thing, but with child pages?

    Thanks

  4. Joe, this works great with WP 2.6.2. Thanks — this was exactly what I was looking for!

  5. The code goes in your theme file sidebar.php or header.php, generally, although it can actually be used in any location in your theme.

    Screenshots would be fairly meaningless, as the code doesn’t define anything about the appearance of the code – just the content of the navigation menu! The appearance will vary enormously depending on your specific usage.

    This code may also be out of date…I’ll check it and make sure it still works with version 2.7, or modify it so that it does.

  6. This post is very interesting, however, I do not understand where to add the code you mentioned? and could you please make some screen shots? It would be easier to understand how the look is going to be… Thanks!

  7. Seker,
    I think Joe has been so clear in his explanation that a .NET developer would certainly have no problems. I only work with HTML and CSS and I could understand with this post easily. Maybe if you had a specific problem?
    Thanks Joe!

  8. Hi Joe,

    I am a .NET developer and very new in php and wordpress. Can you please describe your solution a step by step way for dummies. :)
    Thanks..

Return to Top