WordPress.org

Ready to get started?Download WordPress

Forums

Multilevel horizontal menu (5 posts)

  1. bennysce
    Member
    Posted 4 years ago #

    Hi, i've tried to create a horizontal submenu (here it is: look) but i didn't use the wp hierarchy so the parent isn't selected when sublevels are.

    so my new experiment involves coding a custom walker for wp_nav_menu. Problem is that seems to write the sublevel within the parent ul like so:

    <ul id="menu-standard" class="menu">
     <li id="menu-item-176" class="menu-item menu-item-type-post_type">
     <li id="menu-item-14" class="menu-item menu-item-type-post_type current-menu-item page_item page-item-12 current_page_item">
      <a href="http://85.112.163.17/demo/?page_id=12">Tjänster</a>
      <ul id="submenu">
       <li id="menu-item-237" class="menu-item menu-item-type-post_type">
      </ul>
     </li>
     <li id="menu-item-8" class="menu-item menu-item-type-post_type">
     <li id="menu-item-105" class="menu-item menu-item-type-post_type">
     <li id="menu-item-71" class="menu-item menu-item-type-post_type">
     <li id="menu-item-17" class="menu-item menu-item-type-post_type">
    </ul>

    whereas i'd need it to be like:

    <ul id="menu-standard" class="menu">
     <li id="menu-item-176" class="menu-item menu-item-type-post_type">
     <li id="menu-item-14" class="menu-item menu-item-type-post_type current-menu-item page_item page-item-12 current_page_item">
      <a href="http://85.112.163.17/demo/?page_id=12">Tjänster</a>
     </li>
     <li id="menu-item-8" class="menu-item menu-item-type-post_type">
     <li id="menu-item-105" class="menu-item menu-item-type-post_type">
     <li id="menu-item-71" class="menu-item menu-item-type-post_type">
     <li id="menu-item-17" class="menu-item menu-item-type-post_type">
    </ul>
    <ul id="submenu">
     <li id="menu-item-237" class="menu-item menu-item-type-post_type">
    </ul>

    Is there another technique than walkers to control this or what do you recommend? Has anyone else done something similar?

  2. chrisfullman
    Member
    Posted 3 years ago #

    Anyone? I'm trying to achieve the same thing as bennysce.

  3. asteffensen
    Member
    Posted 3 years ago #

    I also need to have wp_nav_menu output the sub-menu outside the parent ul... There must be someone out there who has done something similar? Or is the wp_nav_menu function too new? :/

  4. asteffensen
    Member
    Posted 3 years ago #

    Hello again.

    I finally just put some money into it and "hired" a guy on wpquestions.com to write a custom walker. This should do it. Only downside is that wordpress wraps it into a ul, so there will be two ul's (menu and sub-menu) inside the menu-ul.

    I hope this helps you out!

    class My_Custom_Walker extends Walker_Nav_Menu {
    
      function walk( $elements, $max_depth) {
    
        global $wp_query;
        $current_page = (int) $wp_query->queried_object_id;
    
        $tops = array(); // top level menu items
        $subs = array(); // sub menu items
    
        _wp_menu_item_classes_by_context($elements);
    
        foreach ($elements as $element) {
          if (0 == $element->menu_item_parent) {
            $tops[] = $element;
            if ($element->object_id == $current_page) {
              $current_parent = $element;
            }
          }
          else
          {
            $subs[] = $element;
    
            if ($element->object_id == $current_page) {
              $current_child = $element;
            }
          }
        }
    
        if (!$current_parent)
    
          $current_parent = $this->get_current_parent_from_sub($elements, $current_child);
    
        if ($current_parent) {
          $real_subs = array();
    
          foreach ($subs as $item) {
            if ($item->menu_item_parent == $current_parent->db_id)
              $real_subs[] = $item;
          }
        }
    
        // show top level elements
    
        $s = '<li><ul id="tops">';
    
        foreach ($tops as $item) {
          $s .= $this->display_item($item);
        }
    
        $s .= "</ul></li>";
    
        // show sub menu items
    
        if (sizeof($real_subs) > 0) {
          $s .= "<li><ul id='subs'>";
          foreach ($real_subs as $item) {
            $s .= $this->display_item($item);
          }
          $s .= '</ul></li>';
        }
        return $s;
      }
    
      // get the parent page when a sub page is the current page
    
      function get_current_parent_from_sub($elements, $current_child) {
    
        foreach ($elements as $element) {
          if ($element->db_id == $current_child->menu_item_parent) {
            return $element;
          }
        }
        return false;
      }
    
      function display_item($item) {
    
        $i = '';
        $this->start_el( $i, $item, 0, array() );
        $this->end_el( $i, $item, 0, array() );
    
        return $i;
    
      }
    }
  5. chrisfullman
    Member
    Posted 3 years ago #

    I actually got this handled, sort of. Not entirely sure how much more this taxes the server to render, but it works for now, and renders two independent UL elements.

    Basically, I'm calling wp_nav_menu() twice, the 2nd one using a custom walker, as such:

    <?php wp_nav_menu(array('theme_location'=>'header-nav', 'container'=>'', 'fallback_cb'=>'', 'depth'=>'1')); ?>
    		<?php wp_nav_menu(array('theme_location'=>'header-nav', 'container'=>'', 'fallback_cb'=>'', 'menu_id'=>'subnav', 'container_id'=>'subnav', 'walker'=> new Extract_Current_Submenu())); ?>

    Then for the custom walker, taken from another tread on this forum by petersom3000:

    class Extract_Current_Submenu extends Walker_Nav_Menu {
    
    	var $found_parents = array();
    
    	function start_el(&$output, $item, $depth, $args) {
    			global $wp_query;
    
    			//this only works for second level sub navigations
    			$parent_item_id = 0;
    
    			$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
    
    			$class_names = $value = '';
    
    			$classes = empty( $item->classes ) ? array() : (array) $item->classes;	
    
    			$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
    			$class_names = ' class="' . esc_attr( $class_names ) . '"';
    			#current_page_item
    
    			// Checks if the current element is in the current selection
    			if (strpos($class_names, 'current-menu-item')
    				|| strpos($class_names, 'current-menu-parent')
    				|| strpos($class_names, 'current-menu-ancestor')
    				|| (is_array($this->found_parents) && in_array( $item->menu_item_parent, $this->found_parents )) ) {
    
    				// Keep track of all selected parents
    				$this->found_parents[] = $item->ID;
    
    				//check if the item_parent matches the current item_parent
    				if($item->menu_item_parent!=$parent_item_id){
    
    					$output .= $indent . '<li id="menu-item-'. $item->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        ) .'"' : '';
    
    					$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 );
    			}
    		}
    
    	function end_el(&$output, $item, $depth) {
    		global $found_parents;
    		// Closes only the opened LI
    		if ( is_array($found_parents) && in_array( $item->ID, $found_parents ) ) {
    			$output .= "</li>\n";
    		}
    	}
    
    	function end_lvl(&$output, $depth) {
    		$indent = str_repeat("\t", $depth);
    		// If the sub-menu is empty, strip the opening tag, else closes it
    		if (substr($output, -22)=="<ul class=\"sub-menu\">\n") {
    			$output = substr($output, 0, strlen($output)-23);
    		} else {
    			$output .= "$indent</ul>\n";
    		}
    	}
    
    }

    That did the trick!

Topic Closed

This topic has been closed to new replies.

About this Topic