Category Archives: bootstrap

Update #4: Using Twitter Bootstrap 2.0 Dropdown Menus with WordPress

Running WordPress on a test instance, it looked like the markup wasn’t quite right.

Essentially, I just added  'container' => false, 'menu_class' => 'nav', options to the menu $args and cleaned the start_el function up a bit.

This stops an extra container div from being created before the ul and adds the nav class to that ul.  The container and everything is left outside of it on purpose, because of all the customizations you may want to do with the responsive options and adding your brand logo… If this was a plugin, there would probably be a options page for handling that stuff, but it remains in loose-code form.

Fixed:
nav class on parent <ul>
clean up code & html

Needs Fixed:
Still not crazy about the code in the start_el function

gist:
gist.github.com/1597994

Update #3: Using Twitter Bootstrap 2.0 Dropdown Menus with WordPress

 

02/17/12 – Updated! Click here for the latest code. After some testing, there were a few fixed to clean up the HTML.

 


Okay, I’ve made a bit of an update to the code.

This should get stock WordPress menus working well with the Twitter Bootstrap 2.0 framework. There are still a few things that need changed, but I need to get a new test instance of WP running first, and I haven’t had the time. We’ll be using it on the station’s site soon, so I imagine it won’t be long before I update it again.

I was thinking of making this a plugin, just to get a plugin out there… not sure.

I’m glad to help anyone with questions, just hit me up in the comments.

Added:
data-toggle attribute
dropdown caret

Needs to be fixed (will be fixed once I get a few minutes to reinstall mamp & such) :
nav class on parent <ul>
clean up code & html

gist:
gist.github.com/1597994

Update #2: Using Twitter Bootstrap Dropdown Menus with WordPress

 

02/17/12 – Updated! Click here for the latest code. After some testing, there were a few fixed to clean up the HTML.

02/13/12 – This code has been updated to work with Twitter Bootstrap 2.0. Click here to see the latest update.

 


Okay, third time is a charm? After finding a trac ticket for the missing has_children parameter here, I’ve made some changes that get the previous extended walker class that I posted working in more (hopefully all) situations. In WordPress 3.4, this should be fixed and rewriting the display_element function shouldn’t be necessary.

The problem with the previous post was that it relied on last_query containing the menu query. I thought that because the menu functions were still running, the menu query would logically be the last query run. This was true on one test theme, but not on another.

Get the gist – https://gist.github.com/1597994

class Bootstrap_Walker_Nav_Menu extends Walker_Nav_Menu {

	function start_lvl( &$output, $depth ) {

		//In a child UL, add the 'dropdown-menu' class
		$indent = str_repeat( "\t", $depth );
		$output	   .= "\n$indent</pre>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">\n";</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">}</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$li_attributes = '';</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$class_names = $value = '';</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$classes = empty( $item->classes ) ? array() : (array) $item->classes;</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">//Add class and attribute to LI element that contains a submenu UL.</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">if ($args->has_children){</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$classes[] = 'dropdown';</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$li_attributes .= 'data-dropdown="dropdown"';</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">}</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$classes[] = 'menu-item-' . $item->ID;</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">//If we are on the current page, add the active class to that menu item.</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$classes[] = ($item->current) ? 'active' : '';</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">//Make sure you still add all of the WordPress classes.</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$class_names = ' class="' . esc_attr( $class_names ) . '"';</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$output .= $indent . '';</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">//Add attributes to link element.</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$attributes = ! empty( $item->attr_title ) ? ' title="' . esc_attr( $item->attr_title ) .'"' : '';</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$attributes .= ! empty( $item->target ) ? ' target="' . esc_attr( $item->target ) .'"' : '';</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) .'"' : '';</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$attributes .= ! empty( $item->url ) ? ' href="' . esc_attr( $item->url ) .'"' : '';</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$attributes .= ($args->has_children) ? ' class="dropdown-toggle"' : '';</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$item_output = $args->before;</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$item_output .= '';</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$item_output .= '';</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$item_output .= $args->after;</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">}</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">//Overwrite display_element function to add has_children attribute. Not needed in >= WordPress 3.4</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">if ( !$element )</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">return;</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$id_field = $this->db_fields['id'];</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">//display this element</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">if ( is_array( $args[0] ) )</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">else if ( is_object( $args[0] ) )</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$args[0]->has_children = ! empty( $children_elements[$element->$id_field] );</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$cb_args = array_merge( array(&$output, $element, $depth), $args);</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">call_user_func_array(array(&$this, 'start_el'), $cb_args);</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$id = $element->$id_field;</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">// descend only when the depth is right and there are childrens for this element</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) {</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">foreach( $children_elements[ $id ] as $child ){</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">if ( !isset($newlevel) ) {</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$newlevel = true;</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">//start the child delimiter</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$cb_args = array_merge( array(&$output, $depth), $args);</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">call_user_func_array(array(&$this, 'start_lvl'), $cb_args);</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">}</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output );</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">}</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">unset( $children_elements[ $id ] );</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">}</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">if ( isset($newlevel) && $newlevel ){</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">//end the child delimiter</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$cb_args = array_merge( array(&$output, $depth), $args);</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">call_user_func_array(array(&$this, 'end_lvl'), $cb_args);</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">}</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">//end this element</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">$cb_args = array_merge( array(&$output, $element, $depth), $args);</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">call_user_func_array(array(&$this, 'end_el'), $cb_args);</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">}</ul>
</ul>
<ul class="\&quot;dropdown-menu\&quot;">
<ul class="\&quot;dropdown-menu\&quot;">}</ul>
</ul>
&nbsp;
<ul class="\&quot;dropdown-menu\&quot;">

Update #1: Using Twitter Bootstrap Dropdown Menus with WordPress

 

02/17/12 – Updated! Click here for the latest code. After some testing, there were a few fixed to clean up the HTML.

02/13/12 – This code has been updated to work with Twitter Bootstrap 2.0. Click here to see the latest update.

01/12/12 – All fixed, see my new post: Update #2: Using Twitter Bootstrap Dropdown Menus with WordPress

 


01/11/12 – It seems that last_result didn’t work as I thought it did and won’t work in all situations, so I need to revisit this. I absentmindedly left my laptop power cable at work, so it may be a day or so.


In my first post, I wrote an extended walker class for using Twitter Bootstrap dropdown menus in WordPress without a bunch of hackarounds. Originally, I was using a ‘search and replace’ method for this, which worked, but it was pretty obvious to me that it wasn’t the best way to do it.

I replaced it now with a more true-to-WordPress way to do it, which I think will be more future-proof.

The reason that this is a bit tricky is that WordPress really doesn’t have a has_children type of property available inside of the start_el function. Instead of doing ‘search and replace’, inside of the start_el function, I pull the last_result, which will a cached copy of the menu query. I’ve seen others run a new query, but I didn’t want to run a new database query for every single menu item if I didn’t have to. I then run through this and check for any entry where the current menu item $item->ID is the parent. I set a has_children boolean so that once I have proof that it’s a daddy (or mommy), I don’t waste effort checking all of the other entries. I then make sure that if it’s the current page it get’s an active class, and apply the dropdown-toggle class to the appropriate link element.

Get the gist – https://gist.github.com/1597994

class Bootstrap_Walker_Nav_Menu extends Walker_Nav_Menu {

	function start_lvl( &$output, $depth ) {

		$indent = str_repeat( "\t", $depth );
		$output	   .= "\n$indent<ul class=\"dropdown-menu\">\n";

	}

	function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
		global $wpdb;
		$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

		$li_attributes = '';
		$has_children = false;
		$class_names = $value = '';

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

		foreach($wpdb->last_result as $a) {
			if ( $a->meta_key == '_menu_item_menu_item_parent' && $a->meta_value == $item->ID && $has_children == false){
				$has_children = true;
				$classes[] = 'dropdown';
				$li_attributes .= 'data-dropdown="dropdown"';
				break;
			}
		}

		$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
		$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 . $li_attributes . '>';

		$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 .= ($has_children) 				? ' class="dropdown-toggle"' 					   : '';

		$item_output = $args->before;
		$item_output .= '<a'. $attributes .'>';
		$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 );
	}

}

Using Bootstrap & WordPress’ Fixed Position Menus Together

When using Bootstrap’s ‘Topbar’ (or any fixed position element) at the top of the page, you’ll have a problem with it and the WordPress ‘Admin bar’ laying atop each other. Here’s a quick and easy fix… provided you follow the examples from twitter.github.com/bootstrap.

.admin-bar .topbar { margin-top: 28px;}

WordPress attaches the admin-bar class to your body tag when you are logged in and the option is set to show the admin bar. Also useful, the logged-in class which gets attached when you are logged in, no matter your ‘admin bar’ preferences. These helper classes can be useful for any number of reasons, but especially here.

For now, it seems the WordPress Admin bar is 28px high. If this changes, you’ll have to edit your CSS to reflect the change. There are certainly other ways to inject the CSS, but this is likely as lightweight as you can get.

Using Twitter Bootstrap Dropdown Menus with WordPress

 

02/17/12 – Updated! Click here for the latest code. After some testing, there were a few fixed to clean up the HTML.

01/12/12 – All fixed, see my new post: Update #2: Using Twitter Bootstrap Dropdown Menus with WordPress

01/11/12 – I’ve since found a better way to handle these and posted a new post here.


Bootstrap is a toolkit from Twitter designed to kickstart development of webapps and sites.
It includes base CSS and HTML for typography, forms, buttons, tables, grids, navigation, and more. – twitter.github.com/bootstrap

I love Twitter’s Bootstrap toolkit. I’ve used it a few times now and I look for every excuse to play around with it. When I use a toolkit like Bootstrap, I really try to leave the toolkit alone and free of modifications. One problem with trying to use Bootstrap and WordPress together is getting the html correct. This goes for a number of things, but doubly for menus. If your html is not correct, the css rules will not take effect and you’ll be left with an unstyled bit of blah.

For example: to use a dropdown menu with Bootstrap, you need to add a
data-dropdown="dropdown" attribute to the appropriate <li> element.

This may not be the best way, but here’s how I did it.
WordPress puts together structured information like this using a “Walker” class. For a menu, that Walker is Walker_Nav_Menu. We will want to extend that class in order to modify how it puts the menu together. Then we’ll make sure that we specify our custom walker when we display our menu.

Here’s we’ll extend the standard walker and add the data-dropdown="dropdown", as well as the neccessary dropdown-toggle class to the link. It goes in your functions.php file.

class Bootstrap_Walker_Nav_Menu extends Walker_Nav_Menu {

function start_lvl( &$output, $depth ) {

$indent = str_repeat( “\t”, $depth );

$li_finder = ‘

    1. jQuery
    2. bootstrap-dropdown.js
    3. script.js

    This is because your script relies on bootstrap.js, which in turn relies on jQuery.

    As anyone will tell you, it’s best practice in situations like this to place your javascript right before your </body> tag.

    There are likely more efficient ways to extend the Walker class, but this is a somewhat less-hacky way than what I’ve seen done. The nice thing is that it works with the stock Bootstrap CSS and there’s no need to do anything in the WordPress backend other than structure your menus as you usually would. It could easily be made into a plugin, I suppose… but it’s a pretty specific, contextual thing.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: