wp-materialize-navwalker icon indicating copy to clipboard operation
wp-materialize-navwalker copied to clipboard

Walker for main and mobile menu

Open pawelos076232 opened this issue 8 years ago • 9 comments

I have navbar like this:

<nav class="grey darken-4" role="navigation">
        <div class="nav-wrapper container"><a id="logo-container" href="<?php echo esc_url( home_url( '/' ) ); ?>" class="brand-logo"><?php bloginfo('name'); ?></a>

            <?php

            $menu = array(
                'theme_location'  => 'primary_header',
                'menu_class'      => 'right hide-on-med-and-down',
                'walker' => new wp_materialize_navwalker()
            );
            $menuMobile = array(
                'theme_location'  => 'primary_header',
                'menu_class'      => 'side-nav',
                'menu_id'         => 'nav-mobile'

            );
            wp_nav_menu($menu);

            wp_nav_menu($menuMobile);

            ?>

            <a href="#" data-activates="nav-mobile" class="button-collapse"><i class="material-icons">menu</i></a>
        </div>
    </nav>

And it is working quite good with Your walker, but when I add second walker to the mobile menu, the main menu brokes. Any idea for this?

pawelos076232 avatar Feb 02 '17 01:02 pawelos076232

Same issue as me any solution for this issue ?

Oaik avatar Jul 12 '17 18:07 Oaik

My English is not so good. but this code that's work for me

<nav role="navigation"> <div class="nav-wrapper container"> <a id="logo-container" href="#" class="brand-logo">Logo</a> <a href="#" data-activates="nav-mobile" class="button-collapse"><i class="fa fa-bars" aria-hidden="true"></i></a> <?php wp_nav_menu( array( 'menu' => 'Primary', 'theme_location'=>'Primary', 'container' => '', 'menu_class' => 'right hide-on-med-and-down', 'walker' => new wp_materialize_navwalker() )); ?> </div> </nav>

and footer.php

`$(function() {

	elemSwap = function() {
		if ($(window).width() < 992) {

			$('.nav-wrapper.container > ul').attr("id", "nav-mobile");
			$('.nav-wrapper.container > ul').removeClass('right hide-on-med-and-down').addClass('side-nav');

		
			$('nav .button-collapse').click(function(event) {
				$('.side-nav').css({
					"transform": "translateX(0%)"
				});
			});

		} else {
			$('.nav-wrapper.container > ul').attr("id", "menu-menu");
			$('.nav-wrapper.container > ul').removeClass('side-nav').addClass('right hide-on-med-and-down');
		}
	}

	elemSwap();

	$(window).resize(elemSwap);
});

`

siwakornkrataipong avatar Sep 09 '17 20:09 siwakornkrataipong

I'm having the same issue. This works with one menu (the desktop). But when I try to use this code to generate the mobile menu as well, the dropdowns stop working.

sveloo avatar Sep 11 '17 04:09 sveloo

my code https://drive.google.com/open?id=0B0Fi5ctV-4KJZFpPTEhBY2xGQ1U

siwakornkrataipong avatar Sep 13 '17 13:09 siwakornkrataipong

I'm going to do update once Materializecss hit stable (1.0) version.

Kail0 avatar Sep 13 '17 14:09 Kail0

Hey, I (kinda) killed 2 birds with one stone here.

So, I'm relatively new to PHP (only just started playing with wordpress functions/php about 3 months ago), so this may not be the best way to do it, but I've addressed the following:

Multiple Navwalkers

To get this to work, you must declare a $GLOBALS variable (in my setup, $GLOBALS['count'] = 0;) before walking the nav, and it's lame, but increment it manually.

<ul class="left hide-on-med-and-down">
        <?php
     		$GLOBALS['count']=0;
           wp_nav_menu( array(
               'menu' => 'top_menu',
               'theme_location'=>'top_menu',
               'menu_class' => 'hide-on-med-and-down',
               'walker' => new wp_materialize_navwalker()
           ));
       ?>
   </ul>
   <ul id="slide-out" class="side-nav blue-grey darken-4">
  	<?php
     $GLOBALS['count']++;
     wp_nav_menu( array(
         'menu' => 'top_menu',
         'theme_location'=>'top_menu',
         'walker' => new wp_materialize_navwalker(),
     ));
   ?>
  </ul>

And then replace your wp_materialize_navwalker.php contents with this

<?php

/**
 * Class Name: wp_materialize_navwalker
 * GitHub URI: #
 * Description: A custom WordPress nav walker class to "fully" implement the Materialize CSS nested navigation style in a custom theme using the WordPress manager
 * Version: 1.0.0
 * Author: Kailo - https://kailo.io
 * Updated: Nov. 2, 2017 by Mackenzie Fritschle as a shoddy compatibility for multiple walkers
 * License: MIT
 * License URI: #
 */

class wp_materialize_navwalker extends Walker {
  var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );

    function start_lvl( &$output, $depth = 0, $args = array() ) {
        // Depth-dependent classes.
        $indent = ( $depth > 0  ? str_repeat( "\t", $depth ) : '' ); // code indent
        $display_depth = ( $depth + 1); // because it counts the first submenu as 0
        $classes = array(
            'dropdown-content',
            ( $display_depth % 2  ? 'menu-odd' : 'menu-even' ),
            ( $display_depth >=2 ? 'sub-sub-menu' : '' ),
            'menu-depth-' . $display_depth
        );
        $class_names = implode( ' ', $classes );

        // Build HTML for output.
        $output .= "\n" . $indent . ' class="' . $class_names . '">' . "\n";
    }

  function end_lvl( &$output, $depth = 0, $args = array() ) {
    $indent = str_repeat("\t", $depth);
    $output .= "$indent</ul>\n";
  }

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

        // Depth-dependent classes.
        $depth_classes = array(
            ( $depth == 0 ? 'main-menu-item' : 'sub-menu-item' ),
            ( $depth >=2 ? 'sub-sub-menu-item' : '' ),
            ( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
            'menu-item-depth-' . $depth
        );
        $depth_class_names = esc_attr( implode( ' ', $depth_classes ) );

        // Passed classes.
        $classes = empty( $item->classes ) ? array() : (array) $item->classes;
        $class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );

    /* Add active class */
    if(in_array('current-menu-item', $classes)) {
      $classes[] = 'active';
      unset($classes['current-menu-item']);
    }

    /* Check for children */
    $children = get_posts(array(
      'post_type' => 'nav_menu_item',
      'nopaging' => true,
      'numberposts' => 1,
      'meta_key' => '_menu_item_menu_item_parent',
      'meta_value' => $item->ID
      ));

    if (!empty($children)) {
      $classes[] = 'dropdown';
    }
    $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
    // $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
    // $id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
    // $id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
    $output     .= $indent . '<li id="nav-menu-item-'.$item->ID.'-'.$GLOBALS['count'].'" class="' . $depth_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        ) .'"' : '';
    $attributes .= ! empty( $children )         ? ' data-activates="dropdown-'. $item->ID.'-'.$GLOBALS['count'] .'"' : '';
    $attributes .= ! empty( $children )         ? ' class="dropdown-button '. $depth_class_names .'"' : '';

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

    if(!empty($children))
      $item_output .= '<i class="material-icons right">arrow_drop_down</i>';

    $item_output .= '</a>';

    $item_output .= $args->after;

    if(!empty($children))
      $item_output .= '<ul id="dropdown-'.$item->ID.'-'.$GLOBALS['count'].'"';
    $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );

  }
  function end_el( &$output, $item, $depth = 0, $args = array() ) {
    $output .= "</li>\n";
  }
}

SideNav Navwalker addition

Create a new file called wp_materialize_navwalker_clps.php, and paste this inside

<?php

/**
 * Class Name: wp_materialize_navwalker_clps
 * GitHub URI: #
 * Description: A custom WordPress nav walker class to "fully" implement the Materialize CSS nested navigation style in a custom theme using the WordPress manager
 * Version: 1.0.0
 * Author: Kailo - https://kailo.io
 * License: MIT
 * License URI: #
 */

class wp_materialize_navwalker_clps extends Walker {
  var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );

     function start_lvl( &$output, $depth = 0, $args = array() ) {
         // Depth-dependent classes.
         $indent = ( $depth > 0  ? str_repeat( "\t", $depth ) : '' ); // code indent
         $display_depth = ( $depth + 1); // because it counts the first submenu as 0
         $classes = array(
             ( $display_depth === 0 ? 'collapsible' : '' ),
             ( $display_depth % 2  ? 'menu-odd' : 'menu-even' ),
             ( $display_depth >=2 ? 'sub-sub-menu' : '' ),
             'menu-depth-' . $display_depth
         );
         $class_names = implode( ' ', $classes );

         // Build HTML for output.
         $output .= "\n" . $indent . ' class="' . $class_names . '">' . "\n";
     }

     function end_lvl( &$output, $depth = 0, $args = array() ) {
       $indent = str_repeat("\t", $depth);
       ($display_depth === 1 ? $output .= "$indent</ul></div></li></ul>\n" : $output .= "$indent</ul></li></ul>\n" );
     }

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

       // Depth-dependent classes.
       $depth_classes = array(
           ( $depth == 0 ? 'main-menu-item' : 'sub-menu-item' ),
           ( $depth >=2 ? 'sub-sub-menu-item' : '' ),
           ( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
           'menu-item-depth-' . $depth
       );
       $depth_class_names = esc_attr( implode( ' ', $depth_classes ) );

       // Passed classes.
       $classes = empty( $item->classes ) ? array() : (array) $item->classes;
       $class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );

       /* Add active class */
       if(in_array('current-menu-item', $classes)) {
         $classes[] = 'active';
         unset($classes['current-menu-item']);
       }

       /* Check for children */
       $children = get_posts(array(
         'post_type' => 'nav_menu_item',
         'nopaging' => true,
         'numberposts' => 1,
         'meta_key' => '_menu_item_menu_item_parent',
         'meta_value' => $item->ID
         ));

       if (!empty($children)) {
         $classes[] = 'collapsible';
       }
       $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );

       $output     .= $indent . '<li id="nav-menu-item-'.$item->ID.'-'.$GLOBALS['count'].'" class="' . $depth_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        ) ? $attributes .= empty( $children         ) ? ' href="'   . esc_attr( $item->url        ) .'"' : '' : ''; //First-level collapsibles don't actually follow the link. I'm not sure of a better way to do this, but you COULD instead put make this normal and use javascript to only activate on doubleclick
       $attributes .= ! empty( $children         ) ? ' class="'. $depth_class_names .'"' : '';

       if( $depth >= 1 ){
         if(!empty($children)){
           $item_output .= '<ul><li>';
           $item_output .= '<div class="subheader">';
         }
       } else {
         if(!empty($children)){
           $item_output .= '<ul class="collapsible"><li>';
           $item_output .= '<div class="collapsible-header">';
         }
       }
       $item_output .= '<a'. $attributes .'>';
       $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;

       if( $depth < 1 )
         if(!empty($children))
           $item_output .= '<i class="fas fa-caret-down"></i>';

       $item_output .= '</a>';

       if(!empty($children))
         $item_output .= '</div>';

       $item_output .= $args->after;

       if(!empty($children))
         //( $depth === 1 ? '<div class="collapsible-body">' : '' );
         ( $depth === 0 ? $item_output .= '<div class="collapsible-body"><ul id="collapsible-'.$item->ID.'-'.$GLOBALS['count'].'"' : $item_output .= '<ul id="collapsible-'.$item->ID.'-'.$GLOBALS['count'].'"' );
         $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
         //( $depth === 1 ? '</div>' : '' );
     }

     function end_el( &$output, $item, $depth = 0, $args = array() ) {
       $output .= "</li>\n";
     }
 }

Follow the same steps as before, but instead of wp_materialize_navwalker, enter wp_materialize_navwalker_clps. So just to skim over, put this in your functions.php

// Register wp-materialize-navwalker
require_once get_template_directory() . '/wp_materialize_navwalker_clps.php';

And use this to walk it like so

<?php
    wp_nav_menu( array(
       'menu' => 'Primary',
       'theme_location'=>'Primary',
       'menu_class' => 'mobile',
       'walker' => new wp_materialize_navwalker_clps()
    ));
?>

I hope this helps anyone for the meantime as we wait for something likely more professional than my code :P

schnoodly avatar Nov 02 '17 22:11 schnoodly

Ah, hang on, let me restructure that comment. I'm not sure how to make different branches (or if I should). Basically, The top block of code is going to be for multiple of the same navwalker, while the bottom is for sideNav (and you can follow the same steps to make multiple, just using the _clps file instead).

So both of those additions work together (and that's what I'm doing on a website), and must be used together of course if you want a normal nav AND a sidenav.

example:

<nav class="blue-grey darken-4">
 <div class="nav-wrapper">
  <ul class="left hide-on-med-and-down">
     <?php
        $GLOBALS['count']=0;
        wp_nav_menu( array(
            'menu' => 'top_menu',
            'theme_location'=>'top_menu',
            'menu_class' => 'hide-on-med-and-down',
            'walker' => new wp_materialize_navwalker()
        ));
    ?>
  </ul>
  <ul id="slide-out" class="side-nav blue-grey darken-4">
    <?php
      $GLOBALS['count']++;
      wp_nav_menu( array(
          'menu' => 'top_menu',
          'theme_location'=>'top_menu',
          'menu_class' => 'mobile',
          'walker' => new wp_materialize_navwalker_clps(),
      ));
    ?>
  </ul>
  <a href="#" data-activates="slide-out" class="button-collapse"><i class="material-icons">menu</i></a>
 </div> 
</nav>

schnoodly avatar Nov 02 '17 22:11 schnoodly

Hello @schnoodly, first, thanks for your soltution - it (kinda) works ^^

Coding a wp theme based on my materialize.css website, had the same issue as you for a nav menu, so I tried your solution, but it doesn't work for me...

I have some dropdown items in my nav and something goes wrong in slide-out mobile menu... I've also noticed that you don't use collapsible-accordion class in your /wp_materialize_navwalker_clps.php....

I'm just trying to understand if I implemented wrong your function or you didn't think about this option..? If it's ok for you, could you attach your fuction.php and init.js files ?

THanks in advance

Aksumiron avatar Jan 05 '18 12:01 Aksumiron

@Aksumiron,

Sorry about the late reply, didn't realize anyone had said anything. Not a fan of GitHub's lack of notification area...

You're right, apparently I didn't have the accordion in there. I've updated the _clps code to add Accordions. Not sure why it wasn't in there at the time, to be honest. I've grown a lot as a developer since then, so completely possible I was doing something contrived and ugly. After this, you should just be able to include it like normal, although you do still have to initialize the javascript for both sidenav and collapsible.

schnoodly avatar Feb 05 '18 18:02 schnoodly