Multiple column lists navigation using wp_nav_menu

 

Intro

Working on a recent project I had to made use of ‘multiple column lists’, based on the article ‘CSS Swag: Multi-Column Lists‘ at A List Apart. For more control over the items in the list, I wanted to use the WordPress custom menus. This is the final result:

Multiple CSS Column List

The CSS

The CSS is pretty straight forward. You can see a basic example here: http://www.alistapart.com/d/multicolumnlists/example6.html

Instead of using a different class for each and every list item, I wanted to useĀ  ‘firstcolumn’, ‘secondcolumn’ and so on for the list items and ‘colheader’ for the items at the top.

/* http://www.alistapart.com/d/multicolumnlists/example6.html */
* html ul li{position:relative;}
ul li{line-height:1.2em;margin:0;padding:0;}
ul li.colheader{margin-top:-104px;} /* I'm using pixels here, but generally it is 'list item count' x 'line-height' (4*1.2) */
ul li.firstcol{margin-left:0em;}
ul li.secondcol{margin-left:160px;} /* a width + padding */
ul li.thirdcol{margin-left:320px;}
ul li a{display:block;width:145px;height:16px;padding:0 0 0 15px;margin:0 0 10px 0;}
ul li a:hover, ul li.current_page_item a{color:#adadad;}

This creates the basic CSS for the list items.

The PHP code

The wp_nav_menu function provides filters and an option to provide your own Walker class which we will use here. Basically, all we need to add is a item counter and add additional CSS classes at certain postitions. Based on the original ‘Walker_Nav_Menu’ class, not much needs to be added:

First, we need to have a variable ‘$item_count’ to keep the list items count:

class my_footer_menu_walker extends Walker {
var $item_count = 0;
/**
* @see Walker::$tree_type
* @since 3.0.0
* @var string
*/
var $tree_type = array( 'post_type', 'taxonomy', 'custom' );
....

Next, we need to modify the function ‘start_el’ by adding the previously declared variable and add our custom CSS classes to the generated list items:

function start_el(&$output, $item, $depth, $args) {
global $wp_query, $item_count;

$indent = ( $depth ) ? str_repeat( "t", $depth ) : '';

$class_names = $value = '';

$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;

# START adding column classes
if($item_count <= 3)
{
$classes[] = 'firstcol';
}

if($item_count == 4)
{
$classes[] = 'colheader';
}

if($item_count > 3 &amp;amp;amp;&amp;amp;amp; $item_count <= 7)
{
$classes[] = 'secondcol';
}

if($item_count == 8)
{
$classes[] = 'colheader';
}

if($item_count > 7 &amp;amp;amp;&amp;amp;amp; $item_count <= 13)
{
$classes[] = 'thirdcol';
}
# END adding column classes

$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
....

Finally, at the end of the function we need to increase the counter:

...
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
$item_count ++;
}

The full code:

# Custom walker class for the wp_nav_menu
class my_footer_menu_walker extends Walker
{
var $item_count = 0;
/**
* @see Walker::$tree_type
* @since 3.0.0
* @var string
*/
var $tree_type = array( 'post_type', 'taxonomy', 'custom' );

/**
* @see Walker::$db_fields
* @since 3.0.0
* @todo Decouple this.
* @var array
*/
var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );

/**
* @see Walker::start_lvl()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param int $depth Depth of page. Used for padding.
*/
function start_lvl(&amp;amp;amp;$output, $depth) {
$indent = str_repeat("t", $depth);
$output .= "n$indent<ul class="sub-menu depth-$depth">n";
}

/**
* @see Walker::end_lvl()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param int $depth Depth of page. Used for padding.
*/
function end_lvl(&amp;amp;amp;$output, $depth) {
$indent = str_repeat("t", $depth);
$output .= "$indent</ul>n";
}

/**
* @see Walker::start_el()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param object $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param int $current_page Menu item ID.
* @param object $args
*/
function start_el(&amp;amp;amp;$output, $item, $depth, $args) {
global $wp_query, $item_count;

$indent = ( $depth ) ? str_repeat( "t", $depth ) : '';

$class_names = $value = '';

$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;

# Hack for adding column classes
if($item_count <= 3)
{
$classes[] = 'firstcol';
}

if($item_count == 4)
{
$classes[] = 'colheader';
}

if($item_count > 3 &amp;amp;amp;&amp;amp;amp; $item_count <= 7)
{
$classes[] = 'secondcol';
}

if($item_count == 8)
{
$classes[] = 'colheader';
}

if($item_count > 7 &amp;amp;amp;&amp;amp;amp; $item_count <= 13)
{
$classes[] = 'thirdcol';
}

$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';

$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
$id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';

$output .= $indent . '<li' . $id . $value . $class_names .'>';

$attributes = ! empty( $item->attr_title ) ? ' title="' . esc_attr( $item->attr_title ) .'"' : '';
$attributes .= ! empty( $item->target ) ? ' target="' . esc_attr( $item->target ) .'"' : '';
$attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) .'"' : '';
$attributes .= ! empty( $item->url ) ? ' href="' . esc_attr( $item->url ) .'"' : '';

# A little hack to disable clicking on links
$is_clickable = get_post_meta($item->object_id, 'link_disabled', true);
$jsarg = ($is_clickable == '1') ? 'onclick="return false;"' : '';

$item_output = $args->before;
$item_output .= '<a'. $attributes .' '.$jsarg.'>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;

$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );

$item_count ++;
}

/**
* @see Walker::end_el()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param object $item Page data object. Not used.
* @param int $depth Depth of page. Not Used.
*/
function end_el(&amp;amp;amp;$output, $item, $depth) {
$output .= "</li>n";
}
}

The Menu

Left to do is tell the wp_nav_menu to use our custom walker class:

wp_nav_menu( array( 'container_class' => '', 'container' => '', 'theme_location' => 'footer', 'walker' => new my_footer_menu_walker ) );

That’s it. ;-)

Please leave your comments, I’d like to know how you modify your menus.

Peter Michael - Lead Developer at Clave Media & founder of Swiss WP. Peter is a WordPress fanatic and blogs rarely on his personal site. You can follow him on Twitter or on Google+

7 Comments


  1. mack
    Aug 12, 2011

    Nice piece of code, shall use it in my next wordpress website!


  2. derek
    Aug 27, 2011

    Does this work on dropdown menues? I have a horizontal navigation bar and I need to split the subpages of a few of the menu items into multiple (2) column. Basically, when you hover over a menu item I want the dropdown to output the sub pages in two columns – is that possible?


  3. kevin
    Sep 01, 2011

    Hey Pete, nice snippet – looks like a lot of work for just a simple side by side list but certainly offers more control. If people don’t care about ugly source you could just use CSS, but the result with this is much cleaner.


    • Pete
      Sep 01, 2011

      Thanks! ;-)


  4. shamas
    Oct 16, 2011

    thanks,cool!

Leave a Reply


Performancing Metrics Blog Statistics

Categories