Wordpress isn’t really designed to be used as a CMS (Content Management System). 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 (Cascading Style Sheets) 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.