Ask your WordPress questions! Pay money and get answers fast! (more info)

Generate nav menu with DIVs instead of ULs (walker_nav_menu?) WordPress

  • SOLVED

I want to have a Wordpress menu generated so that the rendered HTML looks like the template below. The DIVs and classes have to match the HTML below. (Obviously the actual links inside the menu are placeholder, and they would be generated based on what is in the database.)

<div class="menu">
<div class="menu-item">
<strong class="home-icon"></strong>
<a class="menu-enabled" href="#">Home</a>
</div>

<div class="menu-item">
<strong class="features-icon"></strong>
<a class="menu-disabled deploy-submenu" href="#">Features</a>
<div class="clear"></div>
<div class="submenu">
<a href="#">The Drawer Introduction</a> <em class="submenu-decoration"></em>
<a href="#">Typography Features</a> <em class="submenu-decoration"></em>
<a href="#">jQuery Features</a> <em class="submenu-decoration"></em>
<a href="#">Other Features</a>
</div>
</div>

<div class="menu-item">
<strong class="gallery-icon"></strong>
<a class="menu-disabled deploy-submenu" href="#">Gallery</a>
<div class="clear"></div>
<div class="submenu">
<a href="#">Full Width Portfolio</a> <em class="submenu-decoration"></em>
<a href="#">Two Column Portfolio</a> <em class="submenu-decoration"></em>
<a href="#">Swipable Thumbnail Gallery</a> <em class="submenu-decoration"></em>
<a href="#">Filtrable Image Gallery</a>
</div>
</div>

<div class="menu-item">
<strong class="blog-icon"></strong>
<a class="menu-disabled" href="#">Blog</a>
</div>

<div class="menu-item">
<strong class="contact-icon"></strong>
<a class="menu-disabled" href="#">Contact</a>
</div>
</div>


You can see this menu in action (plain HTML) [[LINK href="http://paultrifa.com/envato/codecanyon/drawernav/index.html"]]here[[/LINK]].

I want to have Wordpress generate basically the above HTML using a Wordpress menu.

Is this clear? Can you help me out?

Answers (5)

2013-06-17

Giri answers:

class Custom_Nav_Walker extends Walker_Nav_Menu {

function start_lvl(&$output, $depth = 0, $args = array()) {
$output .= "\n<div class=\"submenu\">\n";
}
function end_lvl(&$output, $depth = 0, $args = array()) {
$output .= "\n</div>\n";
}


function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
parent::start_el($item_html, $item, $depth, $args);
if ($item->is_dropdown && ($depth === 0)) {
$output .= "<div class=\"menu-item\"><a class=\"deploy-submenu\" href=\"".esc_attr($item->url)."\">".esc_attr($item->title)."</a><div class=\"clear\"></div>";
} elseif ($depth === 0) {
$output .= "<div class=\"menu-item\"><a class=\"\" href=\"".esc_attr($item->url)."\">".esc_attr($item->title)."</a>";
} elseif ($depth > 0) {
$output .= "<a href=\"".esc_attr($item->url)."\">".esc_attr($item->title)."</a>";
}
}

function end_el(&$output, $item, $depth=0, $args=array()) {
if ($item->is_dropdown && ($depth === 0)) {
$output .= "</div>\n";
} elseif ($depth === 0) {
$output .= "</div>\n";
} elseif ($depth > 0) {
$output .= "<em class=\"submenu-decoration\"></em>\n";
}

}

}


marct comments:

I dig it! But let me ask an embarrassing question:

What is the code I should use to generate this walker on my page. eg. <?php wp_nav_menu( array( 'theme_location' => 'menu' ) ); ?>


Giri comments:

As for the icon its not possible unless they are same icon.

wp_nav_menu( array('theme_location'=>'menu','walker' => new Custom_Nav_Walker()) );


Giri comments:

Added menu-disabled class. You should use jquery to change that class on click.


class Custom_Nav_Walker extends Walker_Nav_Menu {

function start_lvl(&$output, $depth = 0, $args = array()) {
$output .= "\n<div class=\"submenu\">\n";
}
function end_lvl(&$output, $depth = 0, $args = array()) {
$output .= "\n</div>\n";
}


function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
parent::start_el($item_html, $item, $depth, $args);
if ($item->is_dropdown && ($depth === 0)) {
$output .= "<div class=\"menu-item\"><a class=\"menu-disabled deploy-submenu\" href=\"".esc_attr($item->url)."\">".esc_attr($item->title)."</a><div class=\"clear\"></div>";
} elseif ($depth === 0) {
$output .= "<div class=\"menu-item\"><a class=\"\" href=\"".esc_attr($item->url)."\">".esc_attr($item->title)."</a>";
} elseif ($depth > 0) {
$output .= "<a href=\"".esc_attr($item->url)."\">".esc_attr($item->title)."</a>";
}
}

function end_el(&$output, $item, $depth=0, $args=array()) {
if ($item->is_dropdown && ($depth === 0)) {
$output .= "</div>\n";
} elseif ($depth === 0) {
$output .= "</div>\n";
} elseif ($depth > 0) {
$output .= "<em class=\"submenu-decoration\"></em>\n";
}

}

}


Giri comments:

Small esc_url fix added


class Custom_Nav_Walker extends Walker_Nav_Menu {

function start_lvl(&$output, $depth = 0, $args = array()) {
$output .= "\n<div class=\"submenu\">\n";
}
function end_lvl(&$output, $depth = 0, $args = array()) {
$output .= "\n</div>\n";
}


function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
parent::start_el($item_html, $item, $depth, $args);
if ($item->is_dropdown && ($depth === 0)) {
$output .= "<div class=\"menu-item\"><a class=\"menu-disabled deploy-submenu\" href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a><div class=\"clear\"></div>";
} elseif ($depth === 0) {
$output .= "<div class=\"menu-item\"><a class=\"\" href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a>";
} elseif ($depth > 0) {
$output .= "<a href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a>";
}
}

function end_el(&$output, $item, $depth=0, $args=array()) {
if ($item->is_dropdown && ($depth === 0)) {
$output .= "</div>\n";
} elseif ($depth === 0) {
$output .= "</div>\n";
} elseif ($depth > 0) {
$output .= "<em class=\"submenu-decoration\"></em>\n";
}

}

}


Giri comments:

Sorry I forgot to add one more thing..

You should use wp_nav_menu like this

wp_nav_menu( array('theme_location'=>'menu','items_wrap' => '<div class="menu">%3$s</div>','walker' => new Custom_Nav_Walker()) );


marct comments:

Thanks a lot. I'm trying to make it work on my page. It seems you really understand this and put it together nice. I've been working on it for the past 30 min. I'm making progress and I'll come back to let you know.


Giri comments:

Fixed em tag..

class Custom_Nav_Walker extends Walker_Nav_Menu {
function start_lvl(&$output, $depth = 0, $args = array()) {
$output .= "\n<div class=\"submenu\">\n";
}
function end_lvl(&$output, $depth = 0, $args = array()) {
$output .= "\n</div>\n";
}
function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
parent::start_el($item_html, $item, $depth, $args);
if ($item->is_dropdown && ($depth === 0)) {
$output .= "<div class=\"menu-item\"><a class=\"menu-disabled deploy-submenu\" href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a><div class=\"clear\"></div>";
} elseif ($depth === 0) {
$output .= "<div class=\"menu-item\"><a class=\"menu-disabled\" href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a>";
} elseif ($depth > 0) {
$output .= "<a href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a>";
}
}

function end_el(&$output, $item, $depth=0, $args=array()) {
if ($item->is_dropdown && ($depth === 0)) {
$output .= "</div>\n";
} elseif ($depth === 0) {
$output .= "</div>\n";
} elseif ($depth > 0) {
$output .= "<em class=\"submenu-decoration\"></em>\n";
}
}
}


Giri comments:

Sorry here is the code. Last one missing the closing em tag again

class Custom_Nav_Walker extends Walker_Nav_Menu {
function start_lvl(&$output, $depth = 0, $args = array()) {
$output .= "\n<div class=\"submenu\">\n";
}
function end_lvl(&$output, $depth = 0, $args = array()) {
$output .= "\n</div>\n";
}
function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
parent::start_el($item_html, $item, $depth, $args);
if ($item->is_dropdown && ($depth === 0)) {
$output .= "<div class=\"menu-item\"><a class=\"menu-disabled deploy-submenu\" href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a><div class=\"clear\"></div>";
} elseif ($depth === 0) {
$output .= "<div class=\"menu-item\"><a class=\"menu-disabled\" href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a>";
} elseif ($depth > 0) {
$output .= "<a href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a>";
}
}

function end_el(&$output, $item, $depth=0, $args=array()) {
if ($item->is_dropdown && ($depth === 0)) {
$output .= "</div>\n";
} elseif ($depth === 0) {
$output .= "</div>\n";
} elseif ($depth > 0) {
$output .= "<em class=\"submenu-decoration\"></em>\n";
}
}
}


Giri comments:

I don't know why closing em tag automatically removed when i paste here. Its kind of weird.

[[LINK href="https://gist.github.com/mistergiri/5801194"]]https://gist.github.com/mistergiri/5801194[[/LINK]]


Giri comments:

Hello here, did you try my last answer? (see github link)


marct comments:

Hi Giri, thanks. Sorry my working hours ran up yesterday and I had no move on to other projects. I am back and I'm looking at your code now!


marct comments:

Giri, you mentioned, "As for the icon its not possible unless they are same icon." However, could you use the built-in Wordpress "advanced menu properties" to make this work? Eg. could you use the menu "description" in order to set this line? Do you understand what I mean? Even if the description was "<strong class="home-icon">" it would work.

Can you also build in the "menu-enabled" class for the active top menu item like it is there in the template? Do you get what I mean?

Thank you for the great answer and your work.


marct comments:

Hi Giri,

I see you're in India so it is late for you. I want to stay in touch if you are a WordPress expert because I do a lot of work in WordPress and it would be great to work on more projects together. I have been trying to figure out your code and I even came across questions from you on StackExchange :D

Some of your code isn't working from what you posted on github. I think it could be because the "$item->is_dropdown" part of this code isn't being recognized properly:

if ($item->is_dropdown && ($depth === 0)) {
$output .= "<div class=\"menu-item\"><a class=\"menu-disabled deploy-submenu\" href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a><div class=\"clear\"></div>";
} elseif ($depth === 0) {
$output .= "<div class=\"menu-item\"><a class=\"menu-disabled\" href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a>";
} elseif ($depth > 0) {
$output .= "<a href=\"".esc_url($item->url)."\">".esc_attr($item->title)."</a>";
}


The issue is that the dropdown menu links are being generated with the menu class "menu-disabled", even though they're supposed to have "menu-disabled deploy-submenu". This is making the menu links clickable, and the dropdown is not being deployed! I hope this is clear.

2013-06-17

Alfonso Abarca Alvarez answers:

hi!

insert this code in you're functions.php

<blockquote>
function register_my_menu() {
register_nav_menu('header-menu',__( 'Header Menu' ));
}
add_action( 'init', 'register_my_menu' );
</blockquote>

after this

you can use the administration panel to generate the menu using the interface


marct comments:

This is not the answer.


Alfonso Abarca Alvarez comments:


with this code you get each of the items of your menus

<blockquote>
<nav>
<?php
echo '<h1>prueba de menus </h1>';


$menu_name = 'items';

if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_name ] ) ) {
$menu = wp_get_nav_menu_object( $locations[ $menu_name ] );

$menu_items = wp_get_nav_menu_items($menu->term_id);

$menu_list = '<ul id="menu-' . $menu_name . '">';

foreach ( (array) $menu_items as $key => $menu_item ) {
$title = $menu_item->title;
$url = $menu_item->url;
$menu_list .= '<li><a href="' . $url . '">' . $title . '</a></li>';
}
$menu_list .= '</ul>';
} else {
$menu_list = '<ul><li>Menu "' . $menu_name . '" not defined.</li></ul>';
}
// $menu_list now ready to output

echo $menu_list;


?>

</nav>

</blockquote>

just have to insert them into your template


can you show the structure of your menus in admin panel??




2013-06-17

Hariprasad Vijayan answers:

Hi,

Check the following link, it describes how to customize wordpress menu

http://codex.wordpress.org/Function_Reference/wp_nav_menu#Removing_the_Navigation_Container


marct comments:

Thank you. I have been exploring this topic for about an hour. I was hoping that a developer on this site would generate the necessary code to have Wordpress output/render HTML like in my example (with classes and everything intact.) Is that reasonable or too much work for $25. If someone is familiar with the custom menu functions perhaps it wouldn't be too much work.

2013-06-17

Remy answers:

Yes to modify the html output, you'll have to use a walker in your wp_nav_menu(), extending the Walker_Nav_Menu class. I have a very great resource on this but it's in french


marct comments:

If it is good enough for a beginner/intermediate guy like me, perhaps a Google Translated page would be sufficient for me to figure it out. I've been trying to understand this whole "walker" concept (it's brand new to me). My preference is just to hire someone who knows what they're doing to implement this function for me! :)


Remy comments:

Well, here is the link : http://wabeo.fr/blog/construire-walker-wordpress/

If it's too hard we can maybe discuss an implementation of the function together

2013-06-17

isp_charlie answers:

try custom from here


class theme_extended_walker extends Walker_Nav_Menu{
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
if ( !$element )
return;

$id_field = $this->db_fields['id'];

//display this element
if ( is_array( $args[0] ) )
$args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );

//Adds the 'parent' class to the current item if it has children
if( ! empty( $children_elements[$element->$id_field] ) )
array_push($element->classes,'parent');

$cb_args = array_merge( array(&$output, $element, $depth), $args);

call_user_func_array(array(&$this, 'start_el'), $cb_args);

$id = $element->$id_field;

// descend only when the depth is right and there are childrens for this element
if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) {

foreach( $children_elements[ $id ] as $child ){

if ( !isset($newlevel) ) {
$newlevel = true;
//start the child delimiter
$cb_args = array_merge( array(&$output, $depth), $args);
call_user_func_array(array(&$this, 'start_lvl'), $cb_args);
}
$this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output );
}
unset( $children_elements[ $id ] );
}

if ( isset($newlevel) && $newlevel ){
//end the child delimiter
$cb_args = array_merge( array(&$output, $depth), $args);
call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
}

//end this element
$cb_args = array_merge( array(&$output, $element, $depth), $args);
call_user_func_array(array(&$this, 'end_el'), $cb_args);
}
}



<?php if (has_nav_menu('primary')) wp_nav_menu(array('theme_location' => 'primary', 'depth' => 0, 'container' => '', 'menu_class' => 'pie-css3 clearfix', 'walker' => new theme_extended_walker)); ?>