Created
November 20, 2015 13:38
-
-
Save chhonmeily/23f4c954300b3104daf2 to your computer and use it in GitHub Desktop.
Twitter Bootstrap Walker for WordPress Menu with some simple mega-menu support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Bootstrap Walker for Wordpress Navigation Menu | |
* | |
* Extends WordPress walker class to add support for Twitter Bootstrap navigation menu and JS dropdowns. | |
* Caveats and features: | |
* - Only top level elements with children will be turned into dropdowns | |
* - Can be used in a Bootstrap nav but needs some CSS styling to improve presentation | |
* - With extra CSS multiple columns can be shown in a dropdown (sort of a simple mega-menu) | |
* | |
* Multi column dropdowns (mega-menu) via CSS: | |
* - assign the class .columnizer to top level item in Wordpress | |
* - only columns one level deep are supported (upon opening dropdown) | |
* - each column can have dividers, and further | |
* - in stylesheet, assign: | |
* .columnizer .frame { display: table } | |
* .columnizer .column { display: table-cell } | |
* | |
* @todo improve support for native Bootstrap CSS | |
* @todo improve outputted code indentation | |
*/ | |
class Bootstrap_Walker_Nav_Menu extends Walker_Nav_Menu { | |
// <ul> elements | |
function start_lvl( &$output, $depth ) { | |
// code indentation | |
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); | |
// build classes | |
$classes = array( | |
// only the top level will have dropdowns | |
$depth == 0 ? 'dropdown-menu' : '', | |
// assign a class to items nested deeper | |
$depth >= 1 ? 'sub-menu' : '' | |
); | |
$class_names = implode( ' ', $classes ); | |
// build html - <ul> becomes <div> at top levels | |
if ( $depth == 0 ) $output .= "\n" . $indent . '<div class="' . $class_names . '">' . "\n" . $indent . '<div class="frame">' . "\n"; | |
else $output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n"; | |
} | |
// <li> elements and <a> links | |
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) { | |
// code indentation | |
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : ''; | |
// prepare | |
$li_attributes = ''; | |
$class_names = $value = ''; | |
$classes = empty( $item->classes ) ? array() : (array) $item->classes; | |
// build classes and attributes for <li> elements that have nested <ul> | |
if ( $args->has_children && $depth == 0 ) { | |
$classes[] = 'dropdown'; | |
$li_attributes .= 'data-dropdown="dropdown"'; | |
} | |
$classes[] = 'menu-item-' . $item->ID; | |
// if we are on the current page, add the active class to that menu item | |
// $classes[] = ($item->current) ? 'active' : ''; | |
// add all of the wordpress classes, including those set by user in Wordpress | |
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) ); | |
$class_names = ' class="' . esc_attr( $class_names ) . '"'; | |
// add id to <li> elements | |
$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args ); | |
$id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : ''; | |
// build html - the <li> elements at top level become <div> | |
if ( $depth == 0 ) $output .= $indent . '<li>' . "\n" . $indent . '<div ' . $id . $value . $class_names . $li_attributes . '>'; | |
// | |
elseif ( $depth == 1 ) $output .= $indent . '<div class="column">'; | |
else $output .= $indent . '<li' . $id . $value . $class_names . $li_attributes . '>'; | |
// build attributes and classes for <a> link elements | |
$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 ) .'"' : ''; | |
$attributes .= ( $args->has_children && $depth == 0 ) ? ' class="dropdown-toggle" data-toggle="dropdown"' : ''; | |
// build <a> link html | |
$item_output = $args->before; | |
$item_output .= '<a'. $attributes .'>'; | |
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after; | |
// add a bootstrap "caret" to top level dropdowns toggles | |
$item_output .= ( $args->has_children && $depth == 0 ) ? ' <b class="caret"></b> ' : ''; | |
$item_output .= '</a>'; | |
$item_output .= $args->after; | |
// final html output | |
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args ); | |
} | |
function end_el(&$output, $item, $depth) { | |
// closes elements opened in start_el | |
if ( $depth == 0 ) $output .= '</div>' . "\n" . '</li>' . "\n"; | |
elseif ( $depth == 1 ) $output .= '</div>' . "\n"; | |
else $output .= "</li>\n"; | |
} | |
function end_lvl(&$output, $depth) { | |
// code indentation | |
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); | |
// closes elements opened in start_lvl | |
if ( $depth == 0 ) $output .= $indent . '</div>' . "\n" . $indent . '</div>' . "\n"; | |
else $output .= $indent . '</ul>' . "\n"; | |
} | |
// Overwrite display_element function to add has_children attribute @since WP 3.4 | |
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] ); | |
else if ( is_object( $args[0] ) ) | |
$args[0]->has_children = ! empty( $children_elements[$element->$id_field] ); | |
$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 children 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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.columnizer .frame { display: table } | |
.columnizer .column { display: table-cell } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment