WordPress.org

Ready to get started?Download WordPress

Forums

How do I know if a menu item has children or is a leaf? (8 posts)

  1. otikik
    Member
    Posted 2 years ago #

    Hi there!

    I'm developing a new theme and I need to mark menu items without children differently from "leaf" (no children) ones. Ideally with a css class for the "parents" - has-children, or similar. This is how my walker code looks now:


    class My_Walker_Nav_Menu extends Walker_Nav_Menu {
    function start_el(&$output, $item, $depth, $args) {
    ...
    if(??? $item has children???) {
    // I know what to do here
    }
    }
    }

    What should I put between the ???s ?

    Thanks and regards!

    PS: This question is also in StackOverflow and has a bounty of 100 rep points. Please feel free to answer there and I will gladly grant you those points.

  2. keesiemeijer
    moderator
    Posted 2 years ago #

  3. otikik
    Member
    Posted 2 years ago #

    Hmm. I see. So he can't do it in start_el, he uses the display_element instead. That looks like a possible answer. Thanks!

    PS: please feel free to post that link to the SO question as an answer.

  4. hayatbiralem
    Member
    Posted 2 years ago #

    Hi! I also had a similar problem and figured out just before :)

    The Solution:

    function start_el(&$output, $item, $depth=0, $args=array()) {
    
      $parents = array();
      if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $args->theme_location ] ) ) {
        $menu = wp_get_nav_menu_object( $locations[ $args->theme_location ] );
        $menu_items = wp_get_nav_menu_items($menu->term_id);
        foreach( $menu_items as $menu_item ) {
          if( $menu_item->menu_item_parent != 0 )
            $parents[] = $menu_item->menu_item_parent;
        }
      }
    
      $dropdown = ''; $dropdown_toggle = ''; $caret = '';
      if( in_array($item->ID, $parents ) ) {
        $dropdown = ' dropdown';
        $dropdown_toggle = ' dropdown-toggle';
        $caret = ' <b class="caret"></b>';
      }
    
      $active = $item->current ? ' active' : '';
    
      [...]

    The important point is → if( in_array($item->ID, $parents ) ) { ... }
    I hope that will be useful for you...

    Please Note: In fact, we need only has_children property in $item variable.

  5. ssandison
    Member
    Posted 2 years ago #

    I used the following:

    function start_el(&$output, $item, $depth=0, $args=array()) {
    
    	// Has 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[] = 'no_children';
    	}
    
    	[...]

    Hope this is of use.

  6. hayatbiralem
    Member
    Posted 2 years ago #

    This is a good idea!
    Based on this code can also be used as follows:

    function start_el(&$output, $item, $depth=0, $args=array()) {
    
      global $wpdb;
      $children_count = $wpdb->get_var(
        $wpdb->prepare("
          SELECT COUNT(*) FROM $wpdb->postmeta
          WHERE meta_key = %s
          AND meta_value = %d
        ", '_menu_item_menu_item_parent', $item->ID)
      );
    
      if( $children_count > 0 ) {
        $dropdown = ' dropdown';
        $dropdown_toggle = ' dropdown-toggle';
        $caret = ' <b class="caret"></b>';
      }
    
      [...]
  7. apfelbox
    Member
    Posted 1 year ago #

    I don't like the already posted solutions (except the accepted one on SO), since they issue A LOT of queries to the database, although the necessary data is already available.

    class MyWalker extends Walker_Nav_Menu
    {
        function display_element ($element, &$children_elements, $max_depth, $depth = 0, $args, &$output)
        {
            // check, whether there are children for the given ID and append it to the element with a (new) ID
            $element->hasChildren = isset($children_elements[$element->ID]) && !empty($children_elements[$element->ID]);
    
            return parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output);
        }
    
        function start_el(&$output, $item, $depth, $args)
        {
            global $wp_query;
    
            // you know can access $item->hasChildren to do anything you want..
        }
    }
  8. hayatbiralem
    Member
    Posted 1 year ago #

    Honestly, you're right.

    I just tried to solve the problem by using the start_el function.

    Your solution is more accurate because the performance is more important. Thank you for correcting.

Topic Closed

This topic has been closed to new replies.

About this Topic