各位观众老爷,大家好!我是你们的老朋友,今天咱们来聊聊WordPress的菜单定制这事儿。别以为菜单就是简简单单的链接列表,它可是网站的门面担当,设计得好能让用户眼前一亮,用得顺手,直接提升用户体验。
今天咱们就来深入剖析一下Walker_Nav_Menu
这个类,看看怎么通过继承和重写它的方法,打造出独一无二的导航菜单。
一、 Walker_Nav_Menu
是个啥?
首先,Walker_Nav_Menu
是WordPress核心类,它负责把你在后台设置的菜单项转换成HTML代码。简单来说,就是个“翻译官”,把数据翻译成浏览器看得懂的语言。它使用了一种叫“Walker”的设计模式,像一个辛勤的园丁,一层一层地遍历菜单树,然后根据每个菜单项的属性,生成对应的HTML标签。
二、 为什么要自定义?
WordPress默认的菜单结构虽然够用,但有时候不能满足我们的需求。比如:
- 想要给菜单项添加自定义的class,方便CSS样式控制。
- 想要在菜单项中插入图片或者其他HTML元素。
- 想要改变菜单项的标签结构,比如把
<a>
标签放到<li>
标签里面。 - 想要实现更复杂的菜单效果,比如下拉菜单,Mega Menu等。
总之,想要菜单长成你想要的样子,就得学会自定义。
三、 继承和重写,定制菜单的利器
自定义菜单的关键在于继承Walker_Nav_Menu
类,然后重写它的几个核心方法。就像学武功一样,先学会基础招式,然后才能在此基础上创造自己的绝招。
1. 创建自定义的Walker类
首先,我们要创建一个新的类,继承自Walker_Nav_Menu
。
class My_Custom_Menu_Walker extends Walker_Nav_Menu {
// 这里写我们的自定义代码
}
这就好比我们新建了一个门派,名字叫“My_Custom_Menu_Walker”,以后所有的菜单定制都从这里出发。
2. 重写 start_lvl()
方法
start_lvl()
方法负责输出子菜单的开始标签,比如<ul>
或者<ol>
。我们可以重写这个方法来改变子菜单的HTML结构。
class My_Custom_Menu_Walker extends Walker_Nav_Menu {
function start_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("t", $depth);
$output .= "n$indent<ul class="sub-menu custom-sub-menu">n"; // 添加自定义的class
}
}
这段代码的意思是,在子菜单的<ul>
标签上添加了一个custom-sub-menu
的class。这样我们就可以通过CSS来控制子菜单的样式了。
3. 重写 end_lvl()
方法
end_lvl()
方法负责输出子菜单的结束标签,比如</ul>
或者</ol>
。
class My_Custom_Menu_Walker extends Walker_Nav_Menu {
function end_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("t", $depth);
$output .= "$indent</ul>n";
}
}
这段代码只是简单地输出了</ul>
标签,没有做任何修改。
4. 重写 start_el()
方法
start_el()
方法是整个Walker类中最核心的方法,它负责输出每个菜单项的开始标签,包括<li>
标签和<a>
标签。我们可以重写这个方法来修改菜单项的HTML结构,添加自定义的class,插入图片等等。
class My_Custom_Menu_Walker extends Walker_Nav_Menu {
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
$indent = ( $depth ) ? str_repeat( "t", $depth ) : '';
$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, $depth ) );
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args, $depth );
$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li' . $id . $class_names .'>';
$atts = array();
$atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
$atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
$attributes = '';
foreach ( $atts as $attr => $value ) {
if ( ! empty( $value ) ) {
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
$attributes .= ' ' . $attr . '="' . $value . '"';
}
}
$title = apply_filters( 'the_title', $item->title, $item->ID );
$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 );
}
}
这段代码有点长,我们来分解一下:
$classes
: 获取菜单项的class列表,并添加一个menu-item-{ID}
的class。$class_names
: 把class列表转换成字符串,并添加到<li>
标签上。$id
: 获取菜单项的ID,并添加到<li>
标签上。$atts
: 获取菜单项的属性,比如title
,target
,rel
,href
,并添加到<a>
标签上。$title
: 获取菜单项的标题。$item_output
: 拼接HTML代码,包括<a>
标签,标题,以及$args
中的before
和after
参数。apply_filters( 'walker_nav_menu_start_el', ... )
: 使用walker_nav_menu_start_el
过滤器,允许其他插件或者主题修改菜单项的HTML代码。
重点来了! 我们可以在这个方法中做任何我们想做的事情。比如,我们可以给<a>
标签添加自定义的class:
class My_Custom_Menu_Walker extends Walker_Nav_Menu {
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
// ... 省略之前的代码 ...
$atts = array();
$atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
$atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
$atts['class'] = 'custom-link-class'; // 添加自定义的class
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
// ... 省略之后的代码 ...
}
}
或者,我们可以在菜单项中插入图片:
class My_Custom_Menu_Walker extends Walker_Nav_Menu {
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
// ... 省略之前的代码 ...
$title = apply_filters( 'the_title', $item->title, $item->ID );
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
$item_output .= '<img src="' . get_template_directory_uri() . '/images/menu-icon.png" alt="' . esc_attr( $title ) . '">'; // 插入图片
$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 );
}
}
5. 重写 end_el()
方法
end_el()
方法负责输出每个菜单项的结束标签,比如</li>
标签。
class My_Custom_Menu_Walker extends Walker_Nav_Menu {
function end_el( &$output, $item, $depth = 0, $args = array() ) {
$output .= "</li>n";
}
}
这段代码只是简单地输出了</li>
标签,没有做任何修改。
四、 如何使用自定义的Walker类?
定义好了自定义的Walker类,接下来就要告诉WordPress使用它来生成菜单。这很简单,只需要在wp_nav_menu()
函数中传入'walker' => new My_Custom_Menu_Walker()
参数即可。
wp_nav_menu( array(
'theme_location' => 'primary', // 菜单位置
'menu_class' => 'nav-menu', // 菜单class
'walker' => new My_Custom_Menu_Walker(), // 使用自定义的Walker类
) );
这段代码的意思是,使用My_Custom_Menu_Walker
类来生成primary
位置的菜单。
五、 实战案例:带图标的菜单
我们来做一个稍微复杂一点的例子:给菜单项添加自定义的图标,图标的URL保存在菜单项的描述字段中。
首先,我们需要在后台菜单设置中,打开“描述”字段的显示。在“显示选项”中勾选“描述”即可。
然后,在菜单项的描述字段中填写图标的URL。
最后,修改start_el()
方法:
class My_Custom_Menu_Walker extends Walker_Nav_Menu {
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
// ... 省略之前的代码 ...
$title = apply_filters( 'the_title', $item->title, $item->ID );
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
if ( ! empty( $item->description ) ) {
$item_output .= '<img src="' . esc_url( $item->description ) . '" alt="' . esc_attr( $title ) . '">'; // 插入图标
}
$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 );
}
}
这段代码的意思是,如果菜单项的描述字段不为空,就插入一个<img>
标签,src属性为描述字段的值,alt属性为菜单项的标题。
六、 wp_nav_menu
函数的参数
wp_nav_menu()
函数有很多参数,可以用来控制菜单的输出。下面是一些常用的参数:
参数名 | 描述 |
---|---|
theme_location |
菜单位置,在register_nav_menus() 函数中注册的菜单位置。 |
menu |
菜单ID,菜单名称,或者菜单对象。如果theme_location 参数有效,则忽略此参数。 |
container |
包裹菜单的容器标签,比如div ,nav 等。默认为div 。 |
container_class |
容器标签的class。默认为menu-{菜单名}-container 。 |
container_id |
容器标签的ID。 |
menu_class |
菜单的class。默认为menu 。 |
menu_id |
菜单的ID。 |
echo |
是否输出菜单。默认为true 。如果设置为false ,则返回菜单的HTML代码。 |
fallback_cb |
如果菜单不存在,则调用的函数。默认为wp_page_menu() 。 |
before |
菜单项链接前面的文本。 |
after |
菜单项链接后面的文本。 |
link_before |
菜单项链接文本前面的文本。 |
link_after |
菜单项链接文本后面的文本。 |
depth |
菜单的深度。默认为0,表示不限制深度。 |
walker |
用于生成菜单HTML代码的Walker对象。 |
七、 使用Filter Hooks进行定制
除了继承和重写Walker_Nav_Menu
类,我们还可以使用WordPress提供的Filter Hooks来定制菜单。比如:
wp_nav_menu_objects
: 在菜单项对象被传递给Walker类之前,修改菜单项对象。nav_menu_css_class
: 修改菜单项的CSS class。nav_menu_item_id
: 修改菜单项的ID。nav_menu_link_attributes
: 修改菜单项链接的属性。walker_nav_menu_start_el
: 修改菜单项的HTML代码。
使用Filter Hooks可以避免直接修改Walker_Nav_Menu
类的代码,更加灵活和安全。
八、 总结
今天我们深入探讨了WordPress的菜单定制,学习了如何通过继承和重写Walker_Nav_Menu
类,以及使用Filter Hooks来修改菜单的HTML结构。希望大家能够灵活运用这些技巧,打造出独一无二的导航菜单,提升网站的用户体验。
记住,菜单定制没有固定的套路,只有不断尝试和创新,才能找到最适合自己的解决方案。
好了,今天的讲座就到这里,谢谢大家!如果有什么问题,欢迎留言讨论。 下次再见!