WordPress Cap Top
Wordpress Hacker
 
Alt tag

Auto Create Navigation Tabs for New Pages

Summary:

In this article I explain how you can setup your blog to automatically create main navigation links/tabs when new pages are published by using custom fields to mark those pages you want to appear in the navigation menu.

OK, I’ve had a number of inquiries about setting up navigation tabs to be automatically created when new pages are published (just pages, not posts). Specifically, these inquiries are about my WordPress theme, This Just In!, but this technique can be adapted to any theme to achieve the same effect.

Introduction

OK, so our problem we’re trying to solve here is how to automatically create main navigation tabs when we create new pages so that we don’t have to edit the code every time we want a link to a new page in our main navigation menu.

The main problem we’re going to run into here is that we don’t want to include every single page, otherwise, as you add pages that main navigation menu is going to grow wider than it’s container, which will cause it to wrap to a new line…and that’ll look terrible.

To solve this problem we need to have a way to limit the number of tabs that are automatically added to our navigation menu. Now, we could just limit the number of links to the most that will fit on one line, but that isn’t an ideal solution for two reasons.

First of all, the nav tabs on This Just In! stretch to accommodate the text inside them. So, a limit of 10 links may be OK for one blog if the anchor text is short, but for longer anchor text, those tabs will stretch to accommodate the anchor text within them and 10 tabs may be too much, causing the menu to wrap to a new line. Not good.

The second problem with setting a simple limit is that as we create new pages, we may want some to show up in the main nav menu, but not others. If we set a simple limit on the number of tabs in that menu, we won’t have the control we may need over exactly which pages are included in the navigation menu and which are excluded.

Create a New Category?

My first thought was to create a new category to house all of our pages that we want to appear in the main nav menu and simply throw links to all those pages in that category into the main nav menu. Unfortunately, WordPress doesn’t allow you to assign categories to pages, only to posts, so that idea’s out the window.

Using a Page Parent?

I also considered using a page parent to act like a category. Then we could set the page parent for all the pages we want to show up in the main nav menu, and use wp_list_pages() to list all pages that are children of that particular parent page. This may be a viable, and easy, option for some, but it would cause those pages to show appear with child page styling when wp_list_pages() is used in it’s traditional fashion on themes with unique child page styling. For instance, This Just In! indents child pages under their parent page in the sidebar list of pages. That’s not an ideal solution, so we’ll have to go with something else.

Using Custom Fields

Luckily, WordPress does allow us to assign custom fields to any page. We’ll use a custom field to mark any page that we want to appear in the main nav menu, and later add any page with that custom field to the main nav menu.

The Custom Fields Area

The Custom Fields Area

Using a Custom Function

Once we’ve created our custom fields, we’re going to create a function in functions.php that calls on those custom fields to determine what pages are supposed to show up in the navigation menu.

Action Steps

Creating the Custom Field

First of all, we need to locate any pages we’ve already created that we want to appear in the main nav menu. Edit those pages and add a new custom field…set Key to “NavMenu”, and Value to “yes” (without the quotes).

Custom Field with "NavMenu" key added

Custom Field with "NavMenu" key added

Then select “Add Custom Field” button…once you’ve done that your Custom Fields area should look like Figure 3 here.

After Pressing "Add Custom Field" button

After Pressing "Add Custom Field" button

Just repeat this process for any pages you want to show up in the main navigation menu.

Adding the Function Call in Place of the Current Navigation Menu

For demonstration I’ll be using my This Just In! theme, but you should be able to follow along with any theme that uses a main navigation menu like This Just In!. Now you need to open up header.php in whatever you use to edit PHP files and find navigation menu code block…it looks like this in This Just In!:

<!--Main navigation menu-->
<ul>
<li><a <?php if(is_home()) echo 'class="current" '; ?>href="<?php bloginfo('url'); ?>" title="Home">Home</a></li>
<li><a <?php if(is_archive() || is_page('archives')) echo 'class="current" '; ?>href="<?php bloginfo('url'); ?>/archives/" title="Visit the archives">Archives</a></li>
<li><a <?php if(is_page('about')) echo 'class="current" '; ?>href="<?php bloginfo('url'); ?>/about/" title="About <?php bloginfo('name'); ?>">About</a></li>
<li><a href="<?php bloginfo('url'); ?>/feed/" title="RSS Feed">Feed</a></li>
<li><a href="<?php bloginfo('url'); ?>/sitemap/" title="Sitemap">Sitemap</a></li>
</ul>

Delete everything inside the

    tags and replace it with , so you end up with this:

    <!--Main navigation menu-->
    <ul>
    <?php buildMenu(); ?>
    </ul>

    Create a Function to Build the New Navigation Menu

    If you don’t have a functions.php file in the root directory of your theme, create one now. Then open that file for editing and add the following function:

    // Build navigation menu
    function buildMenu() {
        $pages = get_pages();
        $key = 'NavMenu';
        $pageIDs = array();
        foreach($pages as $page) {
            if(strtolower(get_post_meta($page->ID, $key, true)) == 'yes')
                $pageIDs[] = $page->ID;
        }
        $pageIDList = implode(',', $pageIDs);
        wp_list_pages('include=' . $pageIDList . '&title_li=');
    }

    Be sure your functions.php file has opening and closing tags. If you’re using the latest version of This Just In!, you should already have a functions.php file in your theme directory with something already in it. Just add the buildMenu() function above what’s already in the file so you end up with this:

    <?php
    // Build navigation menu
    function buildMenu() {
        $pages = get_pages();
        $key = 'NavMenu';
        $pageIDs = array();
        foreach($pages as $page) {
            if(strtolower(get_post_meta($page->ID, $key, true)) == 'yes')
                $pageIDs[] = $page->ID;
        }
        $pageIDList = implode(',', $pageIDs);
        wp_list_pages('include=' . $pageIDList . '&title_li=');
    }
    
    if(function_exists('register_sidebar'))
        register_sidebar(array (
            'before_widget' => '<li>',
            'after_widget' => '</li>',
            'before_title' => '<span class="sidetitle">',
            'after_title' => '</span>',
        ));
    ?>

    Highlighting the “Current” Page

    In This Just In!, the current page’s navigation menu item is highlighted with a white background and dark text. Since we’ve removed our original code, which handled that for us, we’ll need to create a new style in our style.css in order to replace that feature.

    In the above code, we used a built-in WordPress function called wp_list_pages() to display a list of pages that were marked with our custom “NavMenu” field. One nice feature about that function is that it returns those pages as list items (<em><li></li></em>) with the current_page_item class applied if that page is currently being viewed. So, all we need to do is add something like the following to styles.css:

    #nav_menu li.current_page_item a {
        background: #FFF;
        color: #333;
    }

    That first #nav_menu part may not be necessary if you’re using a theme other than This Just In!.

    And that’s it, you’ve successfully set up your blog to create your navigation menu links for you. Now all you need to do is when writing a new page that you want to be included in the menu, be sure to add the custom field to that page as we covered earlier.

    If you’d like to understand the code in this tutorial a little better, read on for an explanation.

    Code Explanation

    Ok, let’s take another look at our code:

    // Build navigation menu
    function buildMenu() {
        $pages = get_pages();
        $key = 'NavMenu';
        $pageIDs = array();
        foreach($pages as $page) {
            if(strtolower(get_post_meta($page->ID, $key, true)) == 'yes')
                $pageIDs[] = $page->ID;
        }
        $pageIDList = implode(',', $pageIDs);
        wp_list_pages('include=' . $pageIDList . '&title_li=');
    }

    Now let’s break it down line by line

    // Build navigation menu
    function buildMenu() {

    Comment followed by function declaration.

    $pages = get_pages();

    get_pages() is a built-in WordPress function, that returns an array objects for all of our blog’s pages.

    $key = 'NavMenu';

    Here we set the $key variable to “NavMenu” for use in the get_post_meta function below.

    $pageIDs = array();

    Next we initialize the $pageIDs array, which will hold the page IDs for the pages that have our custom field attached.

    foreach($pages as $page) {

    Simple for-each loop.

    if(strtolower(get_post_meta($page->ID, $key, true)) == 'yes')
                $pageIDs[] = $page->ID;

    The first line uses the get_post_meta() built-in WordPress function. This function takes as parameters 1) a page ID, and 2) a custom field Key, which in our case is “NavMenu”. The third parameter is true to tell the function to return a string of the first occurrence instead of an array of all occurrences of Key. Then we stick that function inside an if-statement to determine if a custom field we’re looking for exists, and we check to see that the custom field contains the value ‘yes’. strtolower() simply converts the string to lowercase just in case you enter something other than all lower case when setting the custom field for the page.

    If that custom field exists, the second line adds the page ID to our $pageIDs array.

    Then the foreach loop continues on to the next page.

    $pageIDList = implode(',', $pageIDs);

    This takes our $pageIDs array with all the page ID numbers of the pages marked with our custom field and strings them all together separated by commas using PHP’s implode() function.. This is so they’re in a format like 2,5,7,10,32 for the following line of code…

    wp_list_pages('include=' . $pageIDList . '&title_li=');

    wp_list_pages() is another built-in WordPress function, which, in this case, takes as parameters 1) a comma-separated list of page ID numbers, which we set up in the last step and 2) &title_li=, which tells WordPress not to return a title for the list of pages. The first parameter is a comma-separated list of page ID numbers that we set up in the previous step.

    Conclusion

    Now that wasn’t too tough was it? Now you know how to set up WordPress to automatically create a navigation menu based on a custom field you assign to pages you want to show up in that navigation menu. We’ve also lightly touched on creating a new function and using the built-in WordPress functions, get_pages(), get_post_meta(), and wp_list_pages(), as well as the php implode() function.

    Print Print Email Email

    54 Responses to “Auto Create Navigation Tabs for New Pages”

    1. Thanks for the heads up on that Tristan, I fixed the ampersand issue. Unfortunately there’s not much I can do about the copy & paste issue. It’s either show the code in an easily readable format like it is, or make it copy and pasteable, but off-hand I don’t know of a way to have both.

    2. Tristan Chambers says:

      There is a minor but important typo in the code examples above.

      In the line that reads:
      wp_list_pages(‘include=’ . $pageIDList . ‘&title_li=’);

      & should be replaced with just a &

      Also to other readers, beware, when I copy and pasted the code from the examples it didn’t work. I had to type it into my text editor. I think there are some funny characters in the code examples that confused my webserver.

      Otherwise. Thanks for the awesome hack. It was just what I was looking for. Your article was very helpful for me, a novice at php.

    3. Sergio Sedas says:

      Thanks for the Code. Great.

      Question – how do I modify to include the original blog page (HOME) into the links – this is where my blog is. And it does not appear.

    4. @Sergio Sedas: You’ll need to hard code a link to the homepage if you want that. See the original header.php file for reference. You should be able to just copy and paste the home link from there.

    5. Alex Teo says:

      hi John,
      I have tried to use your code to change my functions.php, but it does not work properly. Is there a way for you to post a .txt file so I can do a simple copy paste?

      Thanks for all the tips,

    6. Hi John,

      I was attempting to do the above actions but after changing the Header.php code, my page is coming up with just a dark grey background with no content whatsoever. I finished everything else as noted above, but still the same result. Needless to say, I changed everything back to the original code. I don’t think I typed anything in wrong, but I’ve at least figured out that the problem lies in the header code. Any idea what’s going on?

    7. @Alex: The code should work fine, but if you let me know the error you’re receiving, I may be able to help.

      @Jessica McKay: Make sure you’re not deleting any opening or closing html or php tags…that’s what it sounds like.

    8. Darek says:

      Hello,

      Tristan Chambers or John – can you please explain in detail what the following line should contain:

      wp_list_pages(‘include=’ . $pageIDList . ‘&title_li=’);

      ???
      I get the following error – Parse error: parse error in c:\usr\krasnal\www\wp-darek\wp-content\themes\this-just-in\functions.php on line 12

    9. @Darek: That line is using the WordPress template tag, documented at http://codex.wordpress.org/Template_Tags/wp_list_pages to display a list of links to certain pages.

      If you visit the link, you can see that two of the parameters that function (template tag) accepts are “include=” followed by a comma-separated list of page/post ID numbers, and “title_li=” followed by the title to be displayed before the list of links is returned.

      $pageIDList, after the other lines of code, is a variable that contains a comma-separated list of all the page ID numbers for the pages that use the custom field “NavMenu” that we set up in this tutorial.

      So, if you assigned the custom field “NavMenu” to four separate page numbers with IDs 2, 15, 27, and 32, then $pageIDList will end up being 2,15,27,32.

      The period is the standard concatenation operator in PHP, which joins strings, so after all is said and done, in the above example, that line would become this:

      wp_list_pages(’include=2,15,27,32&title_li=’);

      Hope that helps.

    10. Alex Teo says:

      @Darek: I experienced the same problem. I just replaced all the ` by ” and it worked perfectly ( no more parse error). I think there is simply a problem with the copy/paste.

      @John: I managed to get my pages on top tabs ( main nav menu) but it looks like it has to be the same title as my page. This of course is a problem for long titles. I want to keep my long titles for my pages but still be able to put a link to that page in the main nav menu. A long title for my pages is good because it creates a long and precise URL ( good for SEO) but a short nav tab is good for user experience. I see from your blog that you managed to still keep the full name of your pages intact but link them with shorter nav tabs. In your case the example is : name of page, about_wordpresshacker, and the tab says only , about. How do you do it?

    11. @Darek: From Alex Teo’s response, I think I know what’s happening. The plugin I use to color code my php code samples like they are in this article, replaces ‘ with `….in other words…single quote with that slanted quote-thing just above the left tab key on a standard pc keyboard. If you change those quotes to make sure they are single quotes (same key on keyboard as double quote), you should be good.

      @Alex: Yeah, that’s a problem…I don’t use the auto tab creation…my nav links are hard coded. However, you could just change the page slug, which will change the actual URL, but it won’t change the title that appears when viewing that single page.

      If you’re fine with the title on the page itself being short, you can make the link the same, and then just change the page slug for the URL…if you’re looking for both the title on the page itself and the url to be long, and the link short, there’s no easy solution using the automated tab creation…your best be may be to hard code the links.

    12. Alex Teo says:

      thanks for your help John, I would like to keep my page title long and their URL too , simply because it makes it very targeted for SEO. So I guess now that I have learned how to auto create nav tabs, I will go back to the plain hardcoding. Where do I start? in the header?

    13. Darek says:

      Thank you, it works now.

      Another question – button “Home” that was in the Navigation Menu at the very beginning is gone now. Is there any way to put it back – meaning a link to the main page to the NavMenu?

      (my test website can be seen on http://www.dczepiel.yoyo.pl )

    14. @Alex: Look in header.php…if you’re not sure, you can download the theme again and use the original header.php as an example since they’re hard-coded in that.

      @Darek: I answered that question in response to Sergio Sedas above

    15. Scott says:

      I have added per your instructions on all of the pages and now when I go to my site I keep getting syntax errors.

      At first I copied and pasted the code, now I have retyped it all. At first I had a syntax error on line 12 of the functions.php page and now I have a syntax error on line 19.
      this is the error message: Parse error: syntax error, unexpected T_STRING in /home/content/e/d/a/edanportaro/html/cms/wp-content/themes/this-just-in/functions.php on line 19.

      This is line 19: ‘before_title’ => ”,

      Not sure what I should do now, I would appreciate any help. Thanks

    16. @Scott: Copy and paste your code from your functions.php file into notepad or another TEXT ONLY editor, then save it as .txt and send it to me at editor {a t} wordpresshacker [d ot] C o m

    17. Sherin says:

      Excellent guideline. Appreciate the time you have spent to create such nice article I hope, this will be useful for lots of people.

      Do the same work with blogger or is it only for wordpress templates?

      Sherin – Investinternals

    18. Thanks Sherin,

      I’m not sure about blogger as I’ve never tried that before.

    19. Jules says:

      Excellent, thanks for this great enhancement!

      One problem I’m having now is that I cant seem to stop new pages I don’t want in the NavMenu appearing there. I’ve looked for the NavMenu property on the edit page and it doesn’t seem to be added with a value of yes by default so I’ve tried adding NavMenu and setting the value to “no” (without the quotes) but that doesnt get rid of it either. Any ideas how to stop every page appearing there now?

    20. @Jules: The NavMenu property should NOT be added with a value of yes by default. The tutorial explains that. What version of wordpress are you using? Did you copy and paste the code or did you type it in by hand?

    21. Jules says:

      Yeah understood NavMenu shouldn’t be added by default but thought it might have been incorrectly hence checking for it anyway. Wordpress version is 2.6.1 and yes I copied the code but got an error due to the ‘ being incorrect so had to manually change that for the correct ‘ character but apart from that its an exact copy.

    22. @Jules: Make sure the pages that you don’t want to show up do not contain the custom field at all. I just noticed a bug in the code that will cause the page to appear in the nav menu if the NavMenu custom field is set at all (regardless of what it’s set to – yes or no)

      I made an update to the code to solve the problem — Line 7 under “Create a Function to Build the New Navigation Menu” – if it’s not showing up now, give it a bit as the cache has to expire.

    23. Jules says:

      Thanks for the update although it doesn’t solve the problem for me. I replaced line 7 (copied & pasted) but pages with out the NavBar property still appear in the top Menu. Just in case, I also added the custom field and set it to “no” but that too made no difference. Any other suggestions?

    24. Jules says:

      Has anyone managed to get this working yet? I love the feature but I’m stuck now I cant add anymore pages without the NavMenu overfilling :(
      I’d really appreciate it if anyone could assist. Thanks Guys :)

    25. @Jules: Go ahead and send a copy of your functions.php file to me at editor {at] wordpresshacker dot {com} – first, copy the contents of the file into a standard text document (notepad) and then send it as an attachment.

    26. Lyndon says:

      Hi, sounds good. But I have tried to do this trick using this Theme template “Stylized” (www.freewpthemes.net/preview/stylized) and they have bit different “Function Call” in the header.php as follows:

      <li class=”page_item”><a href=”/”>Home

      I have tried to replace with this as you suggested…

      And it did other bits to the end and caused an error with note saying line something in functions.php. I think the ‘menu’ is using includes pull, etc. Do you know the trick to get around to this? I want to add pages but don’t want to add navigagtion menu list (tabs).

      Hear from you soon, thanks dude.

      Lyndon.
      - London UK

    27. Lyndon says:

      Sorry – the part missing code seem to have eased off itself on above – here it is the rest:

      <li class=”page_item”><a href=”/”>Home

      Sorry its all on one line…!

    28. Lyndon says:

      Damn, it still wipe off the other bits of codes that I am trying to show you…. sigh. Let me know how can I show you the code! Maybe download the header.php code from free ’stylized’ them website (www.freewpthemes.net/preview/stylized). Let me know, thanks champ.

    29. davieb says:

      Would it be possible for someone to let me have a copy of the function.php file please? No matter what adjustments I make (as mentioned above) every time I try it I just get

      Parse error: syntax error, unexpected ‘=’ in /home/content/d/a/v/davieb/html/dbpwordpress/wp-content/themes/this-just-in/functions.php on line 12

      Thanks

    30. To everyone having trouble copying and pasting the code: I’ve updated the syntax highlighter so that it doesn’t mess with the quotes like before. Click the “view plain” link to open a popup with plain text code that is easily copy and pasteable.

    31. Josh says:

      How would I add an extra “” in between each nav link?

      I want to add simple vertical dividing bar between each link.

    32. How about border-left: 1px solid black; or something like that in the CSS?

    33. I just replied to your contact form inquiry. One other thing, have you given the navMenu custom field a value of "yes" (without quotes) only on the pages you want to show up in the navigation menu?

      Also, make sure you have this line where it belongs (a previous version of this article had this line written incorrectly and was causing similar errors):
      if(strtolower(get_post_meta($page->ID, $key, true)) == 'yes')

    34. steven says:

      I am having trouble with this code:
      it is showing all pages and not just the ones marked with the navMenu; have coppied the code directly.

      Any ideas?

    35. Hi everybody,
      I’ve just discovered this post and really like the idea of using custom fields for this purpose.

      Maybe you’re interested that I wrote about a dynamic menu that you can manually manage from the widget screen.

    36. CacheWest says:

      <!– Main navigation menu–>
      <ul id="menu">
      <? // This code gets changes the home page tab to current_page_item.
      $this_page = basename($_SERVER['REQUEST_URI']);
      if($this_page == "index.php") { $class='page_item current_page_item'; } else { $class='page_item'; } ?>
      <li class="<?=$class?>">Home</li>
      <?php buildMenu(); ?>
      </ul>
      <!– End Main navigation menu–>

    37. Sebastian says:

      is there a way to implement this functionality so that it shows the categories in the tabbed menu instead of pages?

      • Anything’s possible, but that’s not easily explained based on this example, and, unfortunately, it’s not quite as simple as this. Maybe I’ll do a write-up on that in the future.

    38. Sergio says:

      How could we add “dropdown” menus to the menus ? Wether it be through subpages, or by handwiring the options.
      Thanks,

      Sergio
      PS. Your code works great ! Thx.

      • That’s a topic for it’s own post entirely. I’m actually working on a tutorial for something like that now based on a recent project I did, but it’s a bit complicated and it’s taking me some time to simplify it into something manageable to post here.

    39. casey says:

      The fix worked great. Thanks a lot for providing a simple explanation!

    40. NancyT says:

      I am also trying to get the navigation tabs to only show the pages that have the custom field NavMenu. I see some code was mentioned in your Nov. post; is this what I need, and wher does it go?

      Otherwise loving the theme so far!

      thx

    41. bob says:

      Great template. Do I have to do all of the above to make it so that pages appear in the top navigation bar?

    42. shane says:

      Thank you for all the hard work you have created. A+

      I installed your code to your theme “this just in” and it works great.

      Question: Is there any way to have the page the “current’ page to not be highlighted to indicate that you are still on the “current” page. Thus the “current” page will not have a highlighted background?

      Again, thanks.

      Shane

    43. marc says:

      really needed this article, great theme, thanks again john

    44. Tash says:

      Thanks matey! That hit my confusing question right on the nail. I guess it’s helped by the fact that I’m using your theme now. <3 it, well done.

    45. Sharon says:

      It worked! However, the tabs are arranged alphabetically, and my home tab is in the middle. Is there a way to rearrange?

    46. What does this mean? I have seen other errors, in the comments, but I wasn’t able to find the one I am getting.

      Warning: Cannot modify header information – headers already sent by (output started at /home2/dreadloc/public_html/lives/wp-content/themes/this-just-in/functions.php:2) in /home2/dreadloc/public_html/lives/wp-admin/theme-editor.php on line 70

      I get this every time I try to change anything.

      Thanks so much!!!!

    47. ps. It gives the error on different lines each time, and also it still allows me to make the changes, it just acts like it isn’t going to.

      Any suggestions?

      • Hmm. I’m not sure what that could be offhand. I’m not sure if the register_sidebar function sends header info, but that’s what this error suggests, and that seems to be conflicting with the headers being sent by the theme editor.

        I can’t remember the last time I used the theme editor though so I’m not as well versed with that as the other parts of WordPress. What version of WP and This Just In! are you using?

    48. Steve Liddle says:

      Hi and thanks for a great theme!

      I’m creating several child pages but wonder how to make links in the pages side bar indented.

      • Steve Liddle says:

        For example I have a page called services and then want to have several child pages related to that subject, acoustics, testing, design and so on. It should be obvious that those pages are related to services and not just their own page.

    Leave a Reply




     
    WordPress Cap Top