我正在使用嵌套子菜单开发一个wordpress主题。我需要让没有孩子的元素与有孩子的元素在视觉上不同。现在我有这个菜单,但这可能会改变:
A
a1
a2
B
b1
b2
C
如你所见,A和B有孩子。 C没有 - 我需要它在CSS级别上有所不同。
理想情况下,我希望在A和B中有一个has-children
类,但在C中却没有。
到目前为止,我已经设法创建了一个“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
}
}
}
那么,我如何判断$item
是否有孩子,或者是叶子?
答案 0 :(得分:32)
将此添加到functions.php
,它会将“下拉列表”添加到父级
新的表现方式
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 );
旧:数据库密集
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;
}
答案 1 :(得分:14)
使用这种方式很简单:
说明: 我用“walker”创建菜单:
$walker = new Nav_Walker;
wp_nav_menu(array(
'container'=>'nav',
'container_class'=>'menu',
'menu_class'=>'list-unstyled list-inline',
'walker'=>$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...
}
}
}
我们有对象'walker',您可以 var_dump($ args)来查看更多内容。 我正在使用我的项目!
答案 2 :(得分:9)
似乎问题终于得到了解决。截至当前写作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;
因此,我们不再需要破解function display_element(...)
。
答案 3 :(得分:6)
我在WordPress forum询问,keesiemeijer向我指出this other post,其中他们做了以下事情:
他们修改start_el
而不是修改display_element
,而是添加以下两行(第37-38行here):
//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');
我已经将前两行留作空间参考,并且还作为对此帖中其他答案的评论。似乎wordpress正在“尝试”在$args
中设置'has_children'属性,但它要么做错了,要么以我不理解的方式做错。在任何情况下,has_children
参数永远不会传递给start_el
(参见$ args here的示例var_dump)
这可能是我所获得的Wordpress版本(3.2.1)的错误,可能已在最新版本中修复。
无论如何,我在Wordpress论坛上得到的答案是帮助我解决它的答案,所以我认为这已经解决了。如果keesiemeijer想在这里提出他的答案,我会等待赏金到期。
答案 4 :(得分: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
}
}
}
答案 5 :(得分:3)
Kikito的answer above完成了诀窍,但并非以最可重复使用的方式完成。在我看来,更好的方法是这样的:
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...
}
覆盖Walker::display_element()
是正确的举措,但最好在问题的根源上实际解决问题,而不是简单地在此处修改课程,原因有两个。首先,真正的问题不是缺少类,而是Kikito指出的WordPress中未修补的错误:问题是$args[0]
并不总是数组。这似乎是Walker::display_element()
的典型预期类型,但我们实际上是在这里处理Walker_Nav_Menu::display_element()
,在这种情况下,args
最终作为标准对象类型传入而不是数组类型。因此,我们只需要使用对象表示法而不是数组表示法添加has_children
元素。问题解决了![1]
为普通的Nav Menu案例添加elseif
个帐户。这是同样的形式,希望它成为this patch中的核心类,此时你将不再需要扩展它。他们应该进一步补丁,以解释$args[0]
既不是数组也不是对象的情况,但我不希望看到这种情况发生。
其次,为了在各种方法之间保持良好的关注点分离,应该在start_el()
方法或其他地方添加类,因为display_element()
没有进行任何类处理。
因此,您可以随意覆盖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]这当然是像PHP这样的动态类型语言的潜在缺陷之一......只要你小心,这不是问题。 WordPress开发人员在这里并不小心。
答案 6 :(得分:2)
如果您不想要硬查询或函数的开销,可以在jQuery中执行此操作:
(function() {
// Add 'has_children' class to menus
jQuery.each(jQuery('.menu-item').has('ul.sub-menu'), function() {
jQuery(this).addClass('has_children');
});
})();
答案 7 :(得分:2)
将此添加到您的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;
}
然后在你的步行者中你可以检查$ item-> 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 :(得分:0)
感谢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;
}
生成强> 在wp_get_nav_menu_items函数的foreach中,通过$ item-&gt; menu_item_parent == 0选择父菜单的ID。
这对我很有用,非常简单。
答案 10 :(得分:0)
在你的步行者课程中使用这个简单的代码
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 );
}
}
答案 11 :(得分:0)
由于这个问题是谷歌搜索的第一个结果之一,已在其他网页中被引用,这里的大部分答案已经过时,我想我会发布这个答案。
start_el()将类作为 $ item 对象的一部分。所以你可以在start_el()中添加它:
if(in_array('menu-item-has-children', $item->classes) && $depth != 0)
{
// Your Code
}
请注意,条件中不需要 $ depth ,但如果删除,您的代码将应用于第一项(即深度为0的项目)。
至于兼容性部分,&#39; menu-item-has-children&#39; 已添加到WordPress 3.7 (October 2013),我已在最新的WordPress上测试过它4.4(截至发布此答案时)。
答案 12 :(得分:0)
我们不用重写Walker_Nav_Menu
或Walker
类方法的核心功能,而只是使用良好的旧类继承。
class Has_Child_Walker_Nav_Menu extends Walker_Nav_Menu {
public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
if ( ! $element ) {
return;
}
$element->has_children = ! empty( $children_elements[ $element->{$this->db_fields['id']} ] );
parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
}
Has_Child_Walker_Nav_Menu
,该类继承自Walker_Nav_Menu
,而该类又继承自Walker
。Walker->display_element()
方法,因为它是我们在遍历菜单项树时第一个方法针对每个单独的菜单项。display_element()
方法中,我们在has_children
a.k.a.菜单项上创建了一个新属性$element
。这使得该属性在$item
和start_el()
方法中的end_el()
对象上都可用,这意味着它也传递给了start_el()
所调用的过滤器。has_children
属性值的值的计算方法与确定Walker
实例属性has_children
的方法相同。display_element()
上调用了Walker_Nav_Menu
方法……实际上是祖父母Walker
,因为Walker_Nav_Menu
没有它自己的display_element()
方法。我们确保将所有值从display_element()
方法传递到Walker->display_element()
方法。就是这样!
现在我们有$item->has_children
可用的任何地方,包括过滤器。
菜单
wp_nav_menu(
array(
'theme_location' => 'main_nav',
'container_class' => 'main-nav',
'walker' => new Has_Child_Walker_Nav_Menu()
)
);
过滤器
function my_menu_dropdown( $output, $item, $depth, $args ) {
if ( $item->has_children ) {
$output .= '<a href="#" class="expand-menu-toggle"><i class="fal fa-angle-down"></i></a>';
}
return $output;
}
add_filter( 'walker_nav_menu_start_el', 'my_menu_dropdown', 10, 4 );
答案 13 :(得分:-1)
有一个简单的解决方案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';
}
[...]