Create a gist now

Instantly share code, notes, and snippets.

WordPress Menu & Walker for ZURB's Foundation 4 Top Bar

Adding a Foundation Top Bar to WordPress

foundation-topbar-menu.php and foundation-topbar-walker.php should be required in the WordPress functions.php file.

The code in foundation-topbar.php should be added below the body tag in header.php

Setup a menu in WordPress admin under Appearance > Menus

Use the 'label' class on a menu item to create a menu section title

Foundation Top Bar Doc: http://foundation.zurb.com/docs/components/top-bar.html

<?php
add_theme_support('menus');
/**
* Register Menus
* http://codex.wordpress.org/Function_Reference/register_nav_menus#Examples
*/
register_nav_menus(array(
'top-bar-l' => 'Left Top Bar', // registers the menu in the WordPress admin menu editor
'top-bar-r' => 'Right Top Bar'
));
/**
* Left top bar
* http://codex.wordpress.org/Function_Reference/wp_nav_menu
*/
function foundation_top_bar_l() {
wp_nav_menu(array(
'container' => false, // remove nav container
'container_class' => '', // class of container
'menu' => '', // menu name
'menu_class' => 'top-bar-menu left', // adding custom nav class
'theme_location' => 'top-bar-l', // where it's located in the theme
'before' => '', // before each link <a>
'after' => '', // after each link </a>
'link_before' => '', // before each link text
'link_after' => '', // after each link text
'depth' => 5, // limit the depth of the nav
'fallback_cb' => false, // fallback function (see below)
'walker' => new top_bar_walker()
));
}
/**
* Right top bar
*/
function foundation_top_bar_r() {
wp_nav_menu(array(
'container' => false, // remove nav container
'container_class' => '', // class of container
'menu' => '', // menu name
'menu_class' => 'top-bar-menu right', // adding custom nav class
'theme_location' => 'top-bar-r', // where it's located in the theme
'before' => '', // before each link <a>
'after' => '', // after each link </a>
'link_before' => '', // before each link text
'link_after' => '', // after each link text
'depth' => 5, // limit the depth of the nav
'fallback_cb' => false, // fallback function (see below)
'walker' => new top_bar_walker()
));
}
?>
<?php
/**
* Customize the output of menus for Foundation top bar
*/
class top_bar_walker extends Walker_Nav_Menu {
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
$element->has_children = !empty( $children_elements[$element->ID] );
$element->classes[] = ( $element->current || $element->current_item_ancestor ) ? 'active' : '';
$element->classes[] = ( $element->has_children ) ? 'has-dropdown' : '';
parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {
$item_html = '';
parent::start_el( $item_html, $object, $depth, $args );
$output .= ( $depth == 0 ) ? '<li class="divider"></li>' : '';
$classes = empty( $object->classes ) ? array() : (array) $object->classes;
if( in_array('label', $classes) ) {
$output .= '<li class="divider"></li>';
$item_html = preg_replace( '/<a[^>]*>(.*)<\/a>/iU', '<label>$1</label>', $item_html );
}
if ( in_array('divider', $classes) ) {
$item_html = preg_replace( '/<a[^>]*>( .* )<\/a>/iU', '', $item_html );
}
$output .= $item_html;
}
function start_lvl( &$output, $depth = 0, $args = array() ) {
$output .= "\n<ul class=\"sub-menu dropdown\">\n";
}
}
?>
<div class="top-bar-container fixed contain-to-grid">
<nav class="top-bar">
<ul class="title-area">
<li class="name">
<h1><a href="<?php echo home_url(); ?>"><?php bloginfo('name'); ?></a></h1>
</li>
<li class="toggle-topbar menu-icon"><a href="#"><span>Menu</span></a></li>
</ul>
<section class="top-bar-section">
<?php foundation_top_bar_l(); ?>
<?php foundation_top_bar_r(); ?>
</section>
</nav>
</div>
@xarem
xarem commented Nov 20, 2012

Great - you're my hero! Thank you very much!

@awshout
Owner
awshout commented Dec 6, 2012

GitHub should really notify me of comments on gists! That's not nice of them.

You're welcome! I'm glad this has been helpful to others.

@tiasch
tiasch commented Dec 28, 2012

"As of PHP 5.3.0, you will get a warning saying that "call-time pass-by-reference" is deprecated when you use & in foo(&$a);. And as of PHP 5.4.0, call-time pass-by-reference was removed, so using it will raise a fatal error. "
Quote form from http://php.net/manual/en/language.references.pass.php

in "foundation-topbar-walker.php" on line 13, it should probably be changed from:

parent::display_element($element, &$children_elements, $max_depth, $depth, $args, &$output);

to

parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output);

By doing that I could get rid of the fatal error as I'm using PHP 5.4.4
Does that sound correct?

Thanks for the Snippet awshout!

@awshout
Owner
awshout commented Dec 30, 2012

You're absolutely right. Thanks for catching that!

@Phlow
Phlow commented Feb 8, 2013

Great. You helped me a lot. Now, I just have to get a search form into the right menu by default :)

@awshout
Owner
awshout commented Feb 11, 2013

Glad to help.

For the search form... It will take a bit more work, but to get you started you could use the following in the <section> tag:

<ul class="right"><li><?php get_search_form(); ?></li></ul>
@italiafirenze

I just spent a day hacking around with Reverie to get this to work. Then I find this :-(

Great work though, thanks.

@juandelperal

Awesome!
Thanks!

@ShayHurley

Thanks, just found this and put it to use straight away !!!

@awshout
Owner
awshout commented Feb 28, 2013

You're welcome. I'm happy to see so many using it.

@amrinz
amrinz commented Mar 14, 2013

Thanks awshout, its work well in my theme.

but I dont know why, when i see in responsive layout using web developer, the toggle menu did not work :-)

can you tell me any possibility why this is not work. also what css should i use for content bellow top-bar not-covered by top-bar ?

thanks for your help

@awshout
Owner
awshout commented Mar 14, 2013

Hi @amrinz, it's possible that there is a jQuery conflict.

I'm not sure what you're asking for about the CSS. Follow the grid outline provided by ZURB for any content below the top bar.

@prescottt

Nice one updating this to work with foundation 4, cheers.

@tiasch
tiasch commented Mar 28, 2013

Was it updated to work with foundation 4?

@awshout
Owner
awshout commented Mar 28, 2013

The last two revisions should have updated it to F4. Let me know if I missed anything though.

@JakeSnake

Hey Awshout,
I would like to thank you for this, very awesome that you shared it! Great work, keep it up!

@MrShores

Thank you very much!

@awshout
Owner
awshout commented Jun 20, 2013

You're welcome!

@dafLands
dafLands commented Jul 3, 2013

Thank you sir, you rock!

@iggmack
iggmack commented Jul 4, 2013

Thanks for this! But how to incluce the menu item description into the mix?

@awshout
Owner
awshout commented Jul 9, 2013

@iggmack the top bar isn't really setup to hold the description without some CSS work. To add it into the Walker you'd do something like this in start_el:

if ( !empty( $item->description ) ) {
    $item_html .= '<span class="description">' . esc_attr( $item->description ) . '</span>';
}
@nithino
nithino commented Jul 21, 2013

Thank you. You saved my day. Bookmarking it!

@iggmack
iggmack commented Jul 22, 2013

Thanks!

@MIKendall

Just updated to WordPress 3.6 and got a Strict error regarding start_el: the fix I made is:

function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {

    $item_html = '';
    parent::start_el($item_html, $object, $depth, $args);

    $output .= ($depth == 0) ? '<li class="divider"></li>' : '';

    $classes = empty($object->classes) ? array() : (array) $object->classes;

    if(in_array('label', $classes)) {
        $output .= '<li class="divider"></li>';
        $item_html = preg_replace('/<a[^>]*>(.*)<\/a>/iU', '<label>$1</label>', $item_html);
    }

if ( in_array('divider', $classes) ) {
    $item_html = preg_replace( '/<a[^>]*>( .* )<\/a>/iU', '', $item_html );
}

    $output .= $item_html;
}
@juliosampaio

To get a search form into the right menu by default:


// the right top bar
function foundation_top_bar_r() {
    $menu = wp_nav_menu(array( 
        'echo' => false,
        'container' => false,                           // remove nav container
        'container_class' => '',                // class of container
        'menu' => '',                               // menu name
        'menu_class' => 'top-bar-menu right',           // adding custom nav class
        'theme_location' => 'top-bar-r',                // where it's located in the theme
        'before' => '',                                 // before each link <a> 
        'after' => '',                                  // after each link </a>
        'link_before' => '',                            // before each link text
        'link_after' => '',                             // after each link text
        'depth' => 5,                                   // limit the depth of the nav
        'fallback_cb' => false,                         // fallback function (see below)
        'walker' => new top_bar_walker()
    ));    
     add_search_form($menu); //new function (bellow)
} // end right top bar


function add_search_form($topBar){
    //get the menu and create a php DOMDocument
    $dom = new DOMDocument();
    $dom->loadXML($topBar);
    $xpath = new DOMXPath($dom);
    //get the ul
    $res = $xpath->query("/ul")->item(0);
    //call your search form (notice the false arg)
    $form = get_search_form(false);
    //create a new li tag
    $li_search = $dom->createElement("li");
    //append the search form to a new dom fragment
    $form_html = $dom->createDocumentFragment();    
    $form_html->appendXML($form);    
    $li_search->appendChild($form_html);
    //create de F4 has-form css class and add it to our $li_search
    $class = $dom->createAttribute("class");
    $class->value = "has-form";    
    $li_search->appendChild($class);
    //create a divider li
    $li_divider = $dom->createElement("li");
    $li_divider_class = $dom->createAttribute("class");
    $li_divider_class->value = "divider hide-for-large hide-for-medium";
    $li_divider->appendChild($li_divider_class);
    //more appends
    $res->appendChild($li_divider);
    $res->appendChild($li_search);
    //return our topbar with a search form
    echo $dom->saveXML($res);
}

@awshout
Owner
awshout commented Aug 7, 2013

Thanks for the update @MIKendall

@juliosampaio That's some nice work there. However, I would probably just create a function to output the form for a search input.

function top_bar_search() {

    $output  = '<ul class="right"><li class="has-form">';
    $output .= '<form role="search" method="get" id="searchform" action="' . home_url() . '"><div class="row collapse">';
    $output .= '<div class="large-8 small-8 columns">';
    $output .= '<input type="text" value="' . get_search_query() . '" name="s" id="s" placeholder="' . esc_attr__('Search', 'reactor') . '" />';
    $output .= '</div>';
    $output .= '<div class="large-4 small-4 end columns">';
    $output .= '<input class="button prefix" type="submit" id="searchsubmit" value="' . esc_attr__('Search', 'reactor') . '" />';
    $output .= '</div>';
    $output .= '</div></form>'; 
    $output .= '</li></ul>';

    return $output;
}
@phil2508
phil2508 commented Aug 9, 2013

Would you have any clue why the walker does not get any sub categories? I'm looking at

element->has_children = !empty( $children_elements[$element->ID] );

and it is always empty, although the category clearly has sub categories.

What is going on?

Thanks!

@edequalsawesome

You just saved my sanity. Thanks!

@vajaah
vajaah commented Sep 14, 2013

Was about to write a walker class for this and then voila! Thanks!

@shariffrb

no dropdown in foundation 4.3.1

@webworthydesign

Dropdowns aren't working for me either. :(

@chris-roerig

To fix the drop down issue, add "not-click" class to the "has-dropdown" line.

class top_bar_walker extends Walker_Nav_Menu {

function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
    $element->has_children = !empty( $children_elements[$element->ID] );
    $element->classes[] = ( $element->current || $element->current_item_ancestor ) ? 'active' : '';
    $element->classes[] = ( $element->has_children ) ? 'has-dropdown not-click' : '';

    parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}

....
@genuino2
genuino2 commented Oct 2, 2013

This is the WALKER code with all fix


class top_bar_walker extends Walker_Nav_Menu {
    function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
        $element->has_children = !empty( $children_elements[$element->ID] );
        $element->classes[] = ( $element->current || $element->current_item_ancestor ) ? 'active' : '';
        $element->classes[] = ( $element->has_children ) ? 'has-dropdown not-click' : '';
    
        parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
    }

    function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {
    
        $item_html = '';
        parent::start_el($item_html, $object, $depth, $args);
    
        $output .= ($depth == 0) ? '
  • ' : ''; $classes = empty($object->classes) ? array() : (array) $object->classes; if(in_array('label', $classes)) { $output .= '
  • '; $item_html = preg_replace('/]*>(.*)<\/a>/iU', '$1', $item_html); } if ( in_array('divider', $classes) ) { $item_html = preg_replace( '/]*>( .* )<\/a>/iU', '', $item_html ); } $output .= $item_html; } function start_lvl( &$output, $depth = 0, $args = array() ) { $output .= "\n
      \n"; } }
    @holisticnetworking

    "Notice: Indirect modification of overloaded property WP_Post::$classes has no effect in ../functions.php on line 566"
    "Notice: Trying to get property of non-object in .../wp-includes/nav-menu-template.php on line 88"

    Any guesses? WordPress 3.5.2

    @holisticnetworking

    This hack fixed the above problem for me, though for some reason, I still can't get the menus to display correctly:

    $element->has_children = !empty($children_elements[$element->ID]);
    $classes = $element->classes;
    $classes[] = ($element->current || $element->current_item_ancestor) ? 'active' : '';
    $classes[] = ($element->has_children) ? 'has-dropdown' : '';
    $element->classes = $classes;

    @ScottGrodberg

    The "not-click" class is added by foundation.topbar.js on-the-fly when the menu is in "hover" mode. If you're not seeing the "not-click" class applied to your parent li's at runtime, then maybe check for jQuery conflict like the author said or any other errors in your console. Might save more aggravation later..

    @daniel7912

    Thanks a lot for this, exactly what I was looking for. Saved me loads of time!

    @rffaguiar

    Just as I thought! I was trying to make it work and I thought "someone in the world must have solved this already".
    Put this star at your code.

    @fertchen

    Thanks a lot for sharing this. But...I just can't get this to work. Granted, I don't know a lot about these things. I have done as you advised, included the files, added the script as advised at http://foundation.zurb.com/docs/components/top-bar.html, added the CSS and the menu bar does indeed appear.
    It also turns into the mobile menu when I decrease the browser width (Chrome).

    But upon clicking on the menu, it does not drop down (I did use the code with the fixes by . How can I troubleshoot this?

    I am using this for a Wordpress installtion with a Twenty Twelve Child Theme.

    @sailorsamoor

    Thank you.

    @pulla
    pulla commented Nov 25, 2013

    is there any update for foundation 5? it's not working with foundation 5 now.
    Thanks :)

    @pulla
    pulla commented Nov 25, 2013

    oops just found data-topbar .. it works great

    @thanhrossi

    Not working with Foundation 5? hicc :?

    @PaoloVIP
    PaoloVIP commented Dec 6, 2013

    Works for me with foundation 5 and Wordpress 3.7.1.
    For drop down I added "not-click" like 808chris suggested. Works fine. Thank you

    @StefsterNYC

    Ah this is awesome. I'm so close. Nothing showing up though. Using F5 but so far no luck. What am I missing?

    1. Both .php files are in a inc folder.
    2. Markup in header
    3. Calling both php from functions file.

    Did I miss something?

    @StefsterNYC

    Ah! I got it working. Awesome.

    @maddtechwf

    I'm using F5 also. I have the navigation working but when it gets to a smart phone screen it doesn't work for some reason. Any ideas why?

    ** I FOUND THE FIX. If you downloaded Foundation 5.0.0 then you will have issues. If you download 5.0.1 or 5.0.2 it will fix it for you.

    @Maikenvv

    Thank you so much for this code. I've got Foundation 5.0.2. working with normal nav and mobile. I'm wondering if anyone also had a problem with the dropdown menu's. I can't get them to work. I can click on the parent, and darkens it's color, but no dropdown appears. I've already tried z-index.

    @demo38
    demo38 commented Dec 16, 2013

    Sending positive vibes - because you deserve them. Thanks for taking the time to put this together - hugely useful. @awshout

    @nilocoelhojunior

    Thank you so much for this code

    @StefsterNYC

    I'm not sure if anyone commented on this so I'm just going to throw this out there. When the menu goes below 767px a parent tab that has a page attached to it and children becomes a non usable tab. You click on it and it just slides the menu over to the children rather than actually going to that page. Has anyone else experienced that and if so what was the fix to this? Thanks.

    @bei-soan

    @StefsterNYC I have this issue too, but on every size. Do you have any news about this issue already?

    @mmerriweather

    @aweshout I used the function you wrote to add a search input to the right menu, and it is not showing up in the top bar menu. I added the function inside foundation-topbar-menu.php. Was that the wrong place to add it?

    <div class="top-bar-container fixed contain-to-grid">
                <nav class="top-bar" data-topbar>
                    <ul class="title-area">
                        <li class="name">
                            <h1><a href="<?php echo home_url(); ?>"><?php bloginfo('name'); ?></a></h1>
                        </li>          
                        <li class="toggle-topbar menu-icon"><a href="#"><span>Menu</span></a></li>
                    </ul>
                    <section class="top-bar-section">
                        <?php foundation_top_bar_l(); ?>
                        <?php foundation_top_bar_r(); ?>
                        <ul class="right"><li><?php top_bar_search(); ?></li>
    
    
                    </section>
                </nav>
            </div>
    {
        // code
    }
    @jeremybutler

    Very useful and time saving, thanks for making this public!

    @flowdee
    flowdee commented May 9, 2014

    manually added the "not-click" class which reactivated the dropdown but the mobile/responsive navigation doesn't work any more. suggestions? i click the icon but nothing happens

    @rozeykex

    @flowdee check you have this in your head,

    <meta name="viewport" content="width=device-width">
    @captaincoffee

    Can anyone point to a functioning starter theme that incorporates this? It would be very helpful to see exactly how this is implemented within the various files. Especially for us beginners. Thanks.

    @zerodburn

    Excellent!

    For the search form, or anything at the end of the menu I used this code:

    add_filter( 'wp_nav_menu_items', 'your_custom_menu_item', 10, 2 );
    function your_custom_menu_item ( $items, $args ) {
        if ($args->theme_location == 'top-bar-r') {
            $items .= '<li class="has-form">';
            $items .= '    <form role="search" class="searchform" method="get" action="' . home_url() . '">';
            $items .= '        <div>';
            $items .= '            <input id="sf-s" name="s" value="' . get_search_query() . '" type="text" onkeyup="buttonUp()" placeholder="Search..." />';
            $items .= '            <input id="sf-searchsubmit" type="submit" value="" />';
            $items .= '            <span class="search-icon-toggle" title="Click to Search"></span>';
            $items .= '        </div>';
            $items .= '    </form>';
            $items .= '</li>';
        }
        return $items;
    }
    

    (Obviously use whatever HTML you need)

    @yotawsh
    yotawsh commented Oct 30, 2014

    I'm trying to add the description

    if ( !empty( $item->description ) ) {
        $item_html .= '<span class="description">' . esc_attr( $item->description ) . '</span>';
    }
    

    But I'm unsure of where to place it amongst all this:

    / CUSTOMIZE THE OUTPUT OF MENUS FOR FOUNDATION TOP BAR 
    class top_bar_walker extends Walker_Nav_Menu {
    
        function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
            $element->has_children = !empty( $children_elements[$element->ID] );
            $element->classes[] = ( $element->current || $element->current_item_ancestor ) ? 'active' : '';
            $element->classes[] = ( $element->has_children ) ? 'has-dropdown' : '';
    
            parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
        }
    
        function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {
            $item_html = '';
            parent::start_el( $item_html, $object, $depth, $args ); 
    
            // $output .= ( $depth == 0 ) ? '<li class="divider"></li>' : '';
    
            $classes = empty( $object->classes ) ? array() : (array) $object->classes;  
    
            if( in_array('label', $classes) ) {
                $output .= '<li class="divider"></li>';
                $item_html = preg_replace( '/<a[^>]*>(.*)<\/a>/iU', '<label>$1</label>', $item_html );
            }
    
        if ( in_array('divider', $classes) ) {
            $item_html = preg_replace( '/<a[^>]*>( .* )<\/a>/iU', '', $item_html );
        }
    
            $output .= $item_html;
        }
    
        function start_lvl( &$output, $depth = 0, $args = array() ) {
            $output .= "\n<ul class=\"sub-menu dropdown\">\n";
        }
    
    }
    

    And also don't I have to add it to call it out in my arguments with something like this?

    'walker' => new description_walker()
    

    So far everywhere I've tested adding it, has failed.

    @Warkman
    Warkman commented Nov 27, 2014

    The fix to have the dropdown work in foundation 5 (WP 4.1) is change in foundation4-topbar.php file, line 2:

    from
    <nav class="top-bar">
    to
    <nav class="top-bar" data-topbar>

    @ghost
    ghost commented Dec 30, 2014

    thank you for this awshout, it helped a lot!

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment