Code, Themes

Pure javascript multi-level navigation in WordPress

For some time now I have been using a pure javascript drop down menu script called Drop in my WordPress themes for simple mobile friendly multi-level navigation. I really like it because it doesn’t rely on any external libraries like jQuery. The library was created and is maintained by Chris Ferdindandi.

For the latest versions of Drop (version 6.1.1 at the time of this writing) I have needed to create a custom Walker Nav to add data attributes to list items which have sub-menus, initialize drop with some custom options, and use another small library called Astro (also maintained by Chris, and also written in pure javascript) to expand and collapse the menu on small screens.

I don’t use any of the styles associated with the libraries, just the javascript itself. I do all the styling according to the project or the site design. But I have a forked version of the Underscores starter theme by Automattic where I incorporate Drop and Astro into my own version of Underscores appropriately titled “_S2”. You can find the repo here.

To incorporate Drop and Astro into your WordPress theme you will need to include a custom walker nav. The key part in the walker is adding the data attributes. First you will need to add data-dropdown-menu to the opening ul tag for sub menus. Find the function start_el and modify the output variable like so:

    function start_lvl( &$output, $depth = 0, $args = array() ) {
        $indent = str_repeat("\t", $depth);
        $output .= "\n$indent<ul class=\"sub-menu\" data-dropdown-menu>\n";
    }

In order to check whether a menu item has sub pages you will need to add a function display_element to the class to test for child pages:

    // adds a has_children conditional to $args
    function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {

        $id_field = $this->db_fields['id'];
        if ( is_object( $args[0] ) ) {
            $args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
        }
        return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
    }

Next we will check if the list item has sub pages and if so add the data-dropdown attribute:

        // if the list item has children add a data attribute
        $li_attributes = $args->has_children ? ' data-dropdown' : '';

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

You can find the entire Walker Nav here along with an example of the navigation markup on the front end. To display your navigation on the front end the main thing is to include the Walker Nav in the wp_nav_menu like the following example:

		<nav id="menu" class="nav" role="navigation" itemscope="itemscope" itemtype="http://schema.org/SiteNavigationElement">
			<?php wp_nav_menu(
				array(
					'theme_location' => 'primary',
					'container' => false,
					'menu_id' => 'nav-primary',
					"walker" => new Drop_Menu_Walker(),
				)
			); ?>
        </nav>

In order to toggle the menu on smaller screens I use Astro and a button element to do so:

<button class="nav-toggle" data-nav-toggle="#nav-primary">Menu ▼</button>

You can see that we are targeting the menu id provided in the wp_nav_menu array. Now all that is left is to include the drop and astro files and their dependencies into your theme and initiate like so:

( function() {

	astro.init();

	/**
	 * Initialize Drop for WordPress Menu
	 * https://github.com/cferdinandi/drop
	 */
	drop.init({
	    toggleClass: 'menu-item-has-children', // Class used for the dropdown <li> element
	    contentClass: 'sub-menu', // Class used for the dropdown content <div>
	    toggleActiveClass: 'active', // Class added to active dropdown toggles
	    contentActiveClass: 'active', // Class added to active dropdown content
	});

})();

Drop and Astro use a classList.js polyfill so be sure to include that file as well. Ideally you’d want to concatenate and minify the files into one to reduce requests and make things super fast and rad.

Resources:

S2 Underscores fork: https://github.com/S2web/_s2/tree/drop-astro-genericons
Astro: https://github.com/cferdinandi/astro
Drop: https://github.com/cferdinandi/drop
Class Drop Walker Nav Gist: https://gist.github.com/S2web/53157f2c1adbc7f6c8d9