Wordpress - 我怎么知道菜单项是否有孩子?

时间:2022-05-13 07:30:50

I'm developing a wordpress theme with nested submenus. I need to make the elements with no children visually different from the ones that have children. Right now I have this menu, but that could change:

我正在开发一个带有嵌套子菜单的wordpress主题。我需要让没有孩子的元素在视觉上与有孩子的元素不同。现在我有这个菜单,但可能会改变:

A
  a1
  a2
B
  b1
  b2
C

As you can see, A and B have children. C doesn't - I need it to be different in the CSS level.

如你所见,A和B有孩子。 C没有 - 我需要它在CSS级别上有所不同。

Ideally, I would like to have a has-children class in A and B, but not in C.

理想情况下,我想在A和B中有一个has-children类,但不是在C.

So far I've managed to create a "Menu Walker" PHP class that I can instantiate and pass to wp_nav_menu . Its constructor looks like this:

到目前为止,我已经设法创建了一个“Menu Walker”PHP类,我可以实例化并传递给wp_nav_menu。它的构造函数如下所示:

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
    }
  }
}

So, how do I tell whether $item has children, or is a leaf?

那么,我如何判断$ item是否有孩子,或者是叶子?

EDIT: this question was answered by someone called "keesiemeijer" in the Wordpress forums. I'm leaving this bounty expired just in case he wants to reclaim it. Otherwise, I'll be marking my own answer as valid.

编辑:这个问题是由Wordpress论坛中名为“keesiemeijer”的人回答的。我要离开这个赏金过期以防他想要收回它。否则,我会将自己的答案标记为有效。

13 个解决方案

#1


32  

Add this to functions.php it will add the 'dropdown' class to parents

将它添加到functions.php它会将'dropdown'类添加到父项

New way beter for performance

性能的新方式

function menu_set_dropdown( $sorted_menu_items, $args ) {
    $last_top = 0;
    foreach ( $sorted_menu_items as $key => $obj ) {
        // it is a top lv item?
        if ( 0 == $obj->menu_item_parent ) {
            // set the key of the parent
            $last_top = $key;
        } else {
            $sorted_menu_items[$last_top]->classes['dropdown'] = 'dropdown';
        }
    }
    return $sorted_menu_items;
}
add_filter( 'wp_nav_menu_objects', 'menu_set_dropdown', 10, 2 );

Old: intensive on the DB

旧:密集在DB上

add_filter( 'nav_menu_css_class', 'check_for_submenu', 10, 2);
function check_for_submenu($classes, $item) {
    global $wpdb;
    $has_children = $wpdb->get_var("SELECT COUNT(meta_id) FROM wp_postmeta WHERE meta_key='_menu_item_menu_item_parent' AND meta_value='".$item->ID."'");
    if ($has_children > 0) array_push($classes,'dropdown'); // add the class dropdown to the current list
    return $classes;
}

#2


13  

Simple you use this way:

你用这种方式很简单:

Explain: I create menu with "walker":

说明:我用“walker”创建菜单:

$walker = new Nav_Walker;
wp_nav_menu(array(
        'container'=>'nav',
        'container_class'=>'menu',
        'menu_class'=>'list-unstyled list-inline',
        'walker'=>$walker
    ));

Class Walker:

班沃克:

class Nav_Walker extends Walker_Nav_Menu
{ 
      public function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0)
    {
        if($args->walker->has_children)
        {
            //code...
        }   
    }
}

We have object 'walker', you can var_dump($args) to see more things. I'm using for my project !

我们有对象'walker',你可以使用var_dump($ args)来查看更多内容。我正在使用我的项目!

#3


8  

It seems that the problem has finally been addressed. The latest Wordpress beta as of current writing 4.0, has updated the Walker_Nav_Menu class and added a $has_children property.

似乎问题终于得到了解决。截至当前写作4.0的最新Wordpress测试版更新了Walker_Nav_Menu类并添加了$ has_children属性。

/**
 * Whether the current element has children or not.
 *
 * To be used in start_el().
 *
 * @since 4.0.0
 * @access protected
 * @var bool
 */
protected $has_children;

So, we don't need to hack function display_element(...) anymore.

所以,我们不再需要破解函数display_element(...)了。

#4


6  

I asked in the WordPress forum and keesiemeijer pointed me to this other post, in which they did the following:

我在WordPress论坛上问道,keesiemeijer向我指了另一篇文章,其中他们做了以下内容:

Instead of modifying start_el, they modified display_element, adding the following two lines (lines 37-38 here):

他们修改了display_element而不是修改start_el,添加了以下两行(这里是第37-38行):

//display this element (THESE ARE NOT THE LINES)
if ( is_array( $args[0] ) )
  $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );

// THESE TWO ARE THE LINES:               
if( ! empty( $children_elements[$element->$id_field] ) )
  array_push($element->classes,'parent');

I've left the previous two lines as a spacial reference, and also as a comment to other answers in this post. It seems that wordpress is "trying" to set a ´has_children´ property in $args, but it's either doing it wrong or in a way I don't understand. In any case, that has_children parameter is never passed down to start_el (see sample var_dump of an $args here)

我已将前两行作为空间参考,并在此帖中作为对其他答案的评论。似乎wordpress正在“尝试”在$ args中设置'has_children'属性,但它要么做错了,要么以我不理解的方式做错。在任何情况下,has_children参数永远不会传递给start_el(请参阅此处$ args的示例var_dump)

This might be a bug on the Wordpress version I've got (3.2.1) and might have been fixed in the most recent version.

这可能是我所拥有的Wordpress版本(3.2.1)上的一个错误,可能已在最新版本中修复。

In any case, the answer I got in the Wordpress forum is the one that helped me fix it, so I consider this settled. I'll wait for the bounty to expire just in case keesiemeijer wants to put his answer here.

在任何情况下,我在Wordpress论坛中得到的答案是帮助我解决它的答案,所以我认为这已经解决了。我会等到赏金到期,以防keesiemeijer想把他的答案放在这里。

#5


3  

class My_Walker_Nav_Menu extends Walker_Nav_Menu {
  function start_el(&$output, $item, $depth, $args) {
    ...
    if($args['has_children']) {
      // I know what to do here
    }
  }
}

#6


3  

Kikito's answer above gets the trick done, but not in the most reusable way. In my view, the better approach is like this:

Kikito上面的回答得到了诀窍,但并非以最可重复使用的方式。在我看来,更好的方法是这样的:

function display_element($element, &$children_elements, $max_depth, $depth=0, $args, &$output) {
    // the first few lines of the method...

    //display this element; handle either arrays or objects gracefully
    if ( is_array( $args[0] ) )
        $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );

    elseif ( is_object($args[0]) )
        $args[0]->has_children =  ! empty( $children_elements[$element->$id_field] );

    // the rest of the method...
}

Overriding Walker::display_element() is the right move, but it's better to actually address the problem at the root of the issue rather than simply tacking a class on at this point, for two reasons. First, the real problem isn't a missing class but rather the un-patched bug in WordPress that Kikito noted: the problem is that $args[0] isn't always an array. That appears to be the typically expected type for Walker::display_element(), but we're actually dealing here with Walker_Nav_Menu::display_element(), and in that case args ends up being passed in as a standard object type rather than an array type. As such, we simply need to add the has_children element using object notation instead of array notation. Problem solved![1]

覆盖Walker :: display_element()是正确的举措,但最好在问题的根源处实际解决问题,而不是简单地在此处修改类,原因有两个。首先,真正的问题不是缺少类,而是Kikito指出的WordPress中未修补的错误:问题是$ args [0]并不总是数组。这似乎是Walker :: display_element()的典型预期类型,但我们实际上是在这里处理Walker_Nav_Menu :: display_element(),在这种情况下,args最终作为标准对象类型而不是数组传入类型。因此,我们只需要使用对象表示法而不是数组表示法添加has_children元素。问题解决了![1]

Adding that elseif accounts for the ordinary Nav Menu case. This is the same form that will hopefully make it into the core class in this patch, at which point you'll no longer have to extend it. They should probably patch it further to account for the case that $args[0] is neither an array nor an object, but I don't expect to see that happen.

为普通的Nav Menu案例添加elseif帐户。这是一个相同的形式,希望它成为这个补丁中的核心类,此时你将不再需要扩展它。他们应该进一步修补它以解决$ args [0]既不是数组也不是对象的情况,但我不希望看到这种情况发生。

Second, in order to keep good separation of concerns between the various methods, classes should really be added in the start_el() method or elsewhere, since display_element() isn't doing any of the class handling.

其次,为了在各种方法之间保持良好的关注分离,应该在start_el()方法或其他地方添加类,因为display_element()没有进行任何类处理。

As a result, you can then override start_el() however you like: you can add your own custom classes, or ignore elements entirely, or supply custom text, or whatever you like. (In my case, I'm working around an existing Javascript menu implementation that has very specific classing requirements based on parents and children, so I can't just add the same classes to everything that has a child – which is precisely why this separation of concerns matters.) In my code:

因此,您可以覆盖start_el()但是您可以:您可以添加自己的自定义类,或完全忽略元素,或提供自定义文本或任何您喜欢的内容。 (在我的情况下,我正在解决现有的Javascript菜单实现,该实现具有基于父母和孩子的非常具体的分类要求,所以我不能只为有孩子的所有东西添加相同的类 - 这正是这种分离的原因关注事项。)在我的代码中:

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

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

    $has_children = (is_object($args) && $args->has_children) || (is_array($args) &&  $args['has_children']);
    if ($has_children) {
        // do whatever you need to do
    }

    // everything else the method does...
}

[1] This is of course one of the potential pitfalls of dynamically typed languages like PHP... it's not a problem, as long as you're careful. The WordPress developers weren't careful here.

[1]这当然是像PHP这样的动态类型语言的潜在缺陷之一......只要你小心,这不是问题。 WordPress的开发人员在这里并不小心。

#7


2  

If you don't want the overhead of a hard query or function, you can do this in jQuery:

如果您不想要硬查询或函数的开销,可以在jQuery中执行此操作:

(function() {
    // Add 'has_children' class to menus
    jQuery.each(jQuery('.menu-item').has('ul.sub-menu'), function() {
        jQuery(this).addClass('has_children');
    });
})();

#8


1  

    /**
     * @see My_Nav_Walk::start_el()
     * @since 3.0.0
     *
     * @param string $output Passed by reference. Used to append additional content.
     * @param object $item Menu item data object.
     * @param int $depth Depth of menu item. Used for padding.
     * @param int $current_page Menu item ID.
     * @param object $args
     * @url:http://www.liyinqing.com
     */
class My_Nav_Walk extends Walker_Nav_Menu {
    function start_el(&$output, $item, $depth, $args) {
    global $wp_query;
    $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

    $class_names = $value = '';

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

    $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 .'>';

    $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        ) .'"' : '';

    // Check our custom has_children property.here is the points
    if ( $args->has_children ) {
      $attributes .= ' class="menu parent"';
    }

    $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 display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
    $id_field = $this->db_fields['id'];
    if ( is_object( $args[0] ) ) {/.here is the points
      $args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
    }
    return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
  }

}

}

#9


1  

Add this to your functions.php

将其添加到您的functions.php中

add_filter('wp_nav_menu_objects', 'menu_has_children', 10, 2);

function menu_has_children($sorted_menu_items, $args) {
    $parents = array();
    foreach ( $sorted_menu_items as $key => $obj )
            $parents[] = $obj->menu_item_parent;
    foreach ($sorted_menu_items as $key => $obj)
        $sorted_menu_items[$key]->has_children = (in_array($obj->ID, $parents)) ? true : false;
    return $sorted_menu_items;
}

Then in your walker your can check if $item->has_children is true or false

然后在你的助行器中你可以检查$ item-> has_children是真还是假

#10


1  

Since this question is one of the first results on Google search, has been referenced in other webpages and most the of the answers here are outdated I thought I would post this answer.

由于这个问题是谷歌搜索的第一个结果之一,已在其他网页中被引用,这里的大多数答案已经过时我想我会发布这个答案。

start_el() has classes as part of $item object. So You can add this in start_el():

start_el()将类作为$ item对象的一部分。所以你可以在start_el()中添加它:

   if(in_array('menu-item-has-children', $item->classes) && $depth != 0)
   {
       // Your Code
   }

Note that $depth is not required in the conditions but if removed your code will be applied to the first item (i.e. item with 0 depth).

请注意,条件中不需要$ depth,但如果删除,则您的代码将应用于第一个项目(即具有0深度的项目)。

As for the compatibility part, the class 'menu-item-has-children' has been added to WordPress 3.7 (October 2013) and I have tested it on the latest WordPress 4.4 (as of the time of posting this answer).

至于兼容性部分,“menu-item-has-children”类已被添加到WordPress 3。7(2013年10月),我在最新的WordPress 4.4上测试了它(截至发布此答案时)。

#11


0  

Thanks for Start_el function, my function follow that function to run query. I have a function will count sub menu of parent menu by ID.

感谢Start_el函数,我的函数遵循该函数来运行查询。我有一个功能将按ID计算父菜单的子菜单。

function nav_count_children($parent_id){
    global $wpdb;
    $query = "SELECT COUNT(*) FROM $wpdb->postmeta 
            WHERE meta_key='_menu_item_menu_item_parent' 
            AND meta_value=$parent_id";
    $count_children = $wpdb->get_var( $query );
    return $count_children;
}

Run In foreach of wp_get_nav_menu_items function, select ID of parent Menu by $item->menu_item_parent ==0.

运行在wp_get_nav_menu_items函数的foreach中,通过$ item-> menu_item_parent == 0选择父菜单的ID。

It's working for me and very simple.

它对我有用而且非常简单。

#12


0  

use this simple code in you walker class

在walker类中使用这个简单的代码

 class Description_Walker extends Walker_Nav_Menu
    {
    function start_el(  &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    global $wp_query;
    ////

    ur code in this part 
    ///////
     $depth_classes = array(
        ( $depth == 0 ? 'nav-item' : 'nav-submenu-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 ) ) );


     $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }
    }

#13


-1  

There is a simple solution source.

有一个简单的解决方案来源。

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 ) {
    // has children
    $classes[] = 'parent';
  }

  [...]

#1


32  

Add this to functions.php it will add the 'dropdown' class to parents

将它添加到functions.php它会将'dropdown'类添加到父项

New way beter for performance

性能的新方式

function menu_set_dropdown( $sorted_menu_items, $args ) {
    $last_top = 0;
    foreach ( $sorted_menu_items as $key => $obj ) {
        // it is a top lv item?
        if ( 0 == $obj->menu_item_parent ) {
            // set the key of the parent
            $last_top = $key;
        } else {
            $sorted_menu_items[$last_top]->classes['dropdown'] = 'dropdown';
        }
    }
    return $sorted_menu_items;
}
add_filter( 'wp_nav_menu_objects', 'menu_set_dropdown', 10, 2 );

Old: intensive on the DB

旧:密集在DB上

add_filter( 'nav_menu_css_class', 'check_for_submenu', 10, 2);
function check_for_submenu($classes, $item) {
    global $wpdb;
    $has_children = $wpdb->get_var("SELECT COUNT(meta_id) FROM wp_postmeta WHERE meta_key='_menu_item_menu_item_parent' AND meta_value='".$item->ID."'");
    if ($has_children > 0) array_push($classes,'dropdown'); // add the class dropdown to the current list
    return $classes;
}

#2


13  

Simple you use this way:

你用这种方式很简单:

Explain: I create menu with "walker":

说明:我用“walker”创建菜单:

$walker = new Nav_Walker;
wp_nav_menu(array(
        'container'=>'nav',
        'container_class'=>'menu',
        'menu_class'=>'list-unstyled list-inline',
        'walker'=>$walker
    ));

Class Walker:

班沃克:

class Nav_Walker extends Walker_Nav_Menu
{ 
      public function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0)
    {
        if($args->walker->has_children)
        {
            //code...
        }   
    }
}

We have object 'walker', you can var_dump($args) to see more things. I'm using for my project !

我们有对象'walker',你可以使用var_dump($ args)来查看更多内容。我正在使用我的项目!

#3


8  

It seems that the problem has finally been addressed. The latest Wordpress beta as of current writing 4.0, has updated the Walker_Nav_Menu class and added a $has_children property.

似乎问题终于得到了解决。截至当前写作4.0的最新Wordpress测试版更新了Walker_Nav_Menu类并添加了$ has_children属性。

/**
 * Whether the current element has children or not.
 *
 * To be used in start_el().
 *
 * @since 4.0.0
 * @access protected
 * @var bool
 */
protected $has_children;

So, we don't need to hack function display_element(...) anymore.

所以,我们不再需要破解函数display_element(...)了。

#4


6  

I asked in the WordPress forum and keesiemeijer pointed me to this other post, in which they did the following:

我在WordPress论坛上问道,keesiemeijer向我指了另一篇文章,其中他们做了以下内容:

Instead of modifying start_el, they modified display_element, adding the following two lines (lines 37-38 here):

他们修改了display_element而不是修改start_el,添加了以下两行(这里是第37-38行):

//display this element (THESE ARE NOT THE LINES)
if ( is_array( $args[0] ) )
  $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );

// THESE TWO ARE THE LINES:               
if( ! empty( $children_elements[$element->$id_field] ) )
  array_push($element->classes,'parent');

I've left the previous two lines as a spacial reference, and also as a comment to other answers in this post. It seems that wordpress is "trying" to set a ´has_children´ property in $args, but it's either doing it wrong or in a way I don't understand. In any case, that has_children parameter is never passed down to start_el (see sample var_dump of an $args here)

我已将前两行作为空间参考,并在此帖中作为对其他答案的评论。似乎wordpress正在“尝试”在$ args中设置'has_children'属性,但它要么做错了,要么以我不理解的方式做错。在任何情况下,has_children参数永远不会传递给start_el(请参阅此处$ args的示例var_dump)

This might be a bug on the Wordpress version I've got (3.2.1) and might have been fixed in the most recent version.

这可能是我所拥有的Wordpress版本(3.2.1)上的一个错误,可能已在最新版本中修复。

In any case, the answer I got in the Wordpress forum is the one that helped me fix it, so I consider this settled. I'll wait for the bounty to expire just in case keesiemeijer wants to put his answer here.

在任何情况下,我在Wordpress论坛中得到的答案是帮助我解决它的答案,所以我认为这已经解决了。我会等到赏金到期,以防keesiemeijer想把他的答案放在这里。

#5


3  

class My_Walker_Nav_Menu extends Walker_Nav_Menu {
  function start_el(&$output, $item, $depth, $args) {
    ...
    if($args['has_children']) {
      // I know what to do here
    }
  }
}

#6


3  

Kikito's answer above gets the trick done, but not in the most reusable way. In my view, the better approach is like this:

Kikito上面的回答得到了诀窍,但并非以最可重复使用的方式。在我看来,更好的方法是这样的:

function display_element($element, &$children_elements, $max_depth, $depth=0, $args, &$output) {
    // the first few lines of the method...

    //display this element; handle either arrays or objects gracefully
    if ( is_array( $args[0] ) )
        $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );

    elseif ( is_object($args[0]) )
        $args[0]->has_children =  ! empty( $children_elements[$element->$id_field] );

    // the rest of the method...
}

Overriding Walker::display_element() is the right move, but it's better to actually address the problem at the root of the issue rather than simply tacking a class on at this point, for two reasons. First, the real problem isn't a missing class but rather the un-patched bug in WordPress that Kikito noted: the problem is that $args[0] isn't always an array. That appears to be the typically expected type for Walker::display_element(), but we're actually dealing here with Walker_Nav_Menu::display_element(), and in that case args ends up being passed in as a standard object type rather than an array type. As such, we simply need to add the has_children element using object notation instead of array notation. Problem solved![1]

覆盖Walker :: display_element()是正确的举措,但最好在问题的根源处实际解决问题,而不是简单地在此处修改类,原因有两个。首先,真正的问题不是缺少类,而是Kikito指出的WordPress中未修补的错误:问题是$ args [0]并不总是数组。这似乎是Walker :: display_element()的典型预期类型,但我们实际上是在这里处理Walker_Nav_Menu :: display_element(),在这种情况下,args最终作为标准对象类型而不是数组传入类型。因此,我们只需要使用对象表示法而不是数组表示法添加has_children元素。问题解决了![1]

Adding that elseif accounts for the ordinary Nav Menu case. This is the same form that will hopefully make it into the core class in this patch, at which point you'll no longer have to extend it. They should probably patch it further to account for the case that $args[0] is neither an array nor an object, but I don't expect to see that happen.

为普通的Nav Menu案例添加elseif帐户。这是一个相同的形式,希望它成为这个补丁中的核心类,此时你将不再需要扩展它。他们应该进一步修补它以解决$ args [0]既不是数组也不是对象的情况,但我不希望看到这种情况发生。

Second, in order to keep good separation of concerns between the various methods, classes should really be added in the start_el() method or elsewhere, since display_element() isn't doing any of the class handling.

其次,为了在各种方法之间保持良好的关注分离,应该在start_el()方法或其他地方添加类,因为display_element()没有进行任何类处理。

As a result, you can then override start_el() however you like: you can add your own custom classes, or ignore elements entirely, or supply custom text, or whatever you like. (In my case, I'm working around an existing Javascript menu implementation that has very specific classing requirements based on parents and children, so I can't just add the same classes to everything that has a child – which is precisely why this separation of concerns matters.) In my code:

因此,您可以覆盖start_el()但是您可以:您可以添加自己的自定义类,或完全忽略元素,或提供自定义文本或任何您喜欢的内容。 (在我的情况下,我正在解决现有的Javascript菜单实现,该实现具有基于父母和孩子的非常具体的分类要求,所以我不能只为有孩子的所有东西添加相同的类 - 这正是这种分离的原因关注事项。)在我的代码中:

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

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

    $has_children = (is_object($args) && $args->has_children) || (is_array($args) &&  $args['has_children']);
    if ($has_children) {
        // do whatever you need to do
    }

    // everything else the method does...
}

[1] This is of course one of the potential pitfalls of dynamically typed languages like PHP... it's not a problem, as long as you're careful. The WordPress developers weren't careful here.

[1]这当然是像PHP这样的动态类型语言的潜在缺陷之一......只要你小心,这不是问题。 WordPress的开发人员在这里并不小心。

#7


2  

If you don't want the overhead of a hard query or function, you can do this in jQuery:

如果您不想要硬查询或函数的开销,可以在jQuery中执行此操作:

(function() {
    // Add 'has_children' class to menus
    jQuery.each(jQuery('.menu-item').has('ul.sub-menu'), function() {
        jQuery(this).addClass('has_children');
    });
})();

#8


1  

    /**
     * @see My_Nav_Walk::start_el()
     * @since 3.0.0
     *
     * @param string $output Passed by reference. Used to append additional content.
     * @param object $item Menu item data object.
     * @param int $depth Depth of menu item. Used for padding.
     * @param int $current_page Menu item ID.
     * @param object $args
     * @url:http://www.liyinqing.com
     */
class My_Nav_Walk extends Walker_Nav_Menu {
    function start_el(&$output, $item, $depth, $args) {
    global $wp_query;
    $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

    $class_names = $value = '';

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

    $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 .'>';

    $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        ) .'"' : '';

    // Check our custom has_children property.here is the points
    if ( $args->has_children ) {
      $attributes .= ' class="menu parent"';
    }

    $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 display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
    $id_field = $this->db_fields['id'];
    if ( is_object( $args[0] ) ) {/.here is the points
      $args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
    }
    return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
  }

}

}

#9


1  

Add this to your functions.php

将其添加到您的functions.php中

add_filter('wp_nav_menu_objects', 'menu_has_children', 10, 2);

function menu_has_children($sorted_menu_items, $args) {
    $parents = array();
    foreach ( $sorted_menu_items as $key => $obj )
            $parents[] = $obj->menu_item_parent;
    foreach ($sorted_menu_items as $key => $obj)
        $sorted_menu_items[$key]->has_children = (in_array($obj->ID, $parents)) ? true : false;
    return $sorted_menu_items;
}

Then in your walker your can check if $item->has_children is true or false

然后在你的助行器中你可以检查$ item-> has_children是真还是假

#10


1  

Since this question is one of the first results on Google search, has been referenced in other webpages and most the of the answers here are outdated I thought I would post this answer.

由于这个问题是谷歌搜索的第一个结果之一,已在其他网页中被引用,这里的大多数答案已经过时我想我会发布这个答案。

start_el() has classes as part of $item object. So You can add this in start_el():

start_el()将类作为$ item对象的一部分。所以你可以在start_el()中添加它:

   if(in_array('menu-item-has-children', $item->classes) && $depth != 0)
   {
       // Your Code
   }

Note that $depth is not required in the conditions but if removed your code will be applied to the first item (i.e. item with 0 depth).

请注意,条件中不需要$ depth,但如果删除,则您的代码将应用于第一个项目(即具有0深度的项目)。

As for the compatibility part, the class 'menu-item-has-children' has been added to WordPress 3.7 (October 2013) and I have tested it on the latest WordPress 4.4 (as of the time of posting this answer).

至于兼容性部分,“menu-item-has-children”类已被添加到WordPress 3。7(2013年10月),我在最新的WordPress 4.4上测试了它(截至发布此答案时)。

#11


0  

Thanks for Start_el function, my function follow that function to run query. I have a function will count sub menu of parent menu by ID.

感谢Start_el函数,我的函数遵循该函数来运行查询。我有一个功能将按ID计算父菜单的子菜单。

function nav_count_children($parent_id){
    global $wpdb;
    $query = "SELECT COUNT(*) FROM $wpdb->postmeta 
            WHERE meta_key='_menu_item_menu_item_parent' 
            AND meta_value=$parent_id";
    $count_children = $wpdb->get_var( $query );
    return $count_children;
}

Run In foreach of wp_get_nav_menu_items function, select ID of parent Menu by $item->menu_item_parent ==0.

运行在wp_get_nav_menu_items函数的foreach中,通过$ item-> menu_item_parent == 0选择父菜单的ID。

It's working for me and very simple.

它对我有用而且非常简单。

#12


0  

use this simple code in you walker class

在walker类中使用这个简单的代码

 class Description_Walker extends Walker_Nav_Menu
    {
    function start_el(  &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    global $wp_query;
    ////

    ur code in this part 
    ///////
     $depth_classes = array(
        ( $depth == 0 ? 'nav-item' : 'nav-submenu-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 ) ) );


     $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }
    }

#13


-1  

There is a simple solution source.

有一个简单的解决方案来源。

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 ) {
    // has children
    $classes[] = 'parent';
  }

  [...]