Show a WP 3.0 Custom Menu in an HTML Select with Auto-Navigation?

As part of a theme for a client, I want to be able to show a custom menu (defined via the admin) in a select box that automatically changes pages after changing the selection.

Is there a plugin (or a handy code snippet) that will accomplish this?

Related posts

Leave a Reply

1 comment

  1. WordPress’ new menu system is both wondrous and infinitely frustrating, depending on what you are trying to do and what day of the week it happens to be. 🙂 It’s a great idea but far from mature so while it’s a feature I applaud I’ll be happier when v3.3 or v3.4 of WordPress rolls out and we get a lot more use-cases directly supported by the menu system’s API.

    That said, not sure if there is an existing plugin to do what you are looking for, but how about the code you need to write you own plugin? Or you could just include it in your theme’s functions.php file; whatever your preference.

    What I’m providing is a fully self-contained example that you can save as test.phpin your website’s root directory in order to test it. If your domain were example.com you’d load to test at:

    http://example.com/test.php

    Here’s what it look’s like in action:

    Inactive drop down of Pages from WordPress 3.0 Menu
    (source: mikeschinkel.com)
    Active drop down of Pages from WordPress 3.0 Menu
    (source: mikeschinkel.com)

    From the code below it should be easy to incorporate the get_page_selector() function into your theme and to call it whenever you need this functionality:

    <?php
    
      include "wp-load.php";
      echo 'Jump to:';
      echo get_page_selector('My Select Menu');
    
    function get_page_selector($menu) {
      $page_menu_items = wp_get_nav_menu_items($menu,array(
        'meta_key'=>'_menu_item_object',
        'meta_value'=>'page',
      ));
      $selector = array();
      if (is_array($page_menu_items) && count($page_menu_items)>0) {
        $selector[] =<<<HTML
    <select id="page-selector" name="page-selector"
        onchange="location.href = document.getElementById('page-selector').value;">
    HTML;
        $selector[] = '<option value="">Select a Page</option>';
        foreach($page_menu_items as $page_menu_item) {
          $link = get_page_link($page_menu_item->object_id);
          $selector[] =<<<HTML
    <option value="{$link}">{$page_menu_item->title}</option>
    HTML;
      }
        $selector[] = '</select>';
      }
      return implode("n",$selector);
    }
    

    So you might wonder how it works?

    The wp_get_nav_menu_items() function

    WordPress 3.0 stores it’s menus in the wp_posts table as post_type type of nav_menu_item. The wp_get_nav_menu_items() indirectly just calls the get_posts() function. The first parameter to wp_get_nav_menu_items() is either 1.) a menu name (which is what I used: “My Select Menu”), 2.) a menu item ID (i.e. the menu item’s post ID the database) or 3.) a menu slug (the slug from the menu’s taxonomy term; yes menus are implemented using taxonomy terms with a taxonomy of 'nav_menu'.)

    Menu Configuration in WordPress 3.0
    (source: mikeschinkel.com)

    Beyond the first parameter it forwards on to get_posts() most (if not all?) of the $args you pass to wp_get_nav_menu_items() thus you can treat it like a custom post type (even though longer term when they improve the menu API that probably won’t be such a great idea. But today? Make hay while the sun shines!)

    Filtering Menu Items with meta_key and meta_value

    WordPress’ underlying use of posts for menu items is why we can query for meta_key and meta_value; WordPress uses a series of meta_keys prefixed with _menu_item for the additional information it needs for each menu item. _menu_item_object will contain page for every menu item that corresponds to a WordPress “Page” post type. (If you want to include items besides Pages you you’ll need to do a bit more research than I did here but at least I gave you the tools you need to do the research yourself.)

    Here’s a screenshot using Navicat for MySQL of a query showing the meta records for several nav_menu_items:

    Menu Item Configuration found in wp_postmeta with WordPress 3.0
    (source: mikeschinkel.com)

    Grabbing the Page’s URL with get_post_link()

    Next I’ll point out that get the page’s URL from the get_post_link() function and that I’m setting the HTML <option>‘s value to be the URL…

    <?php
        $link = get_page_link($page_menu_item->object_id);
        $selector[] =<<<HTML
    <option value="{$link}">{$page_menu_item->title}</option>
    HTML;
    

    Using Javascript’s onchange to Navigate to our Selected Page

    …So that I can grab it from the value property of the 'page-selector' <select> element and assign it to location.href. Assigning location.href causes the browser to immediately navigate to the new URL, and that, in a nutshell, is how it’s all done:

    <?php
      $selector[] =<<<HTML
    <select id="page-selector" name="page-selector"
        onchange="location.href = document.getElementById('page-selector').value;">
    HTML;
    

    An Empty value="" as the Default Option

    You might note that the “Select a Page” default option has an empty value; that’s not a mistake but instead by design. When it is selected and the "onchange" is triggered1 setting location.href to an empty string will have no effect, which is exactly what we want and it doesn’t require us to write an exception code. Viola!

    <?php
        $selector[] = '<option value="">Select a Page</option>';
    
    1. Having selection of “Select a Page” trigger "onchange" can only happen on a browser back navigation to a page that doesn’t trigger a page reload where another option had previously been selected, but it can still happen so we should address it.