wp_nav_menu()
Outputs the menu created in the admin panel: "Appearance > Menus".
Which specific menu to display (there can be several) is specified in the parameter theme_location or menu.
To enable menu support in the theme, this feature must be activated using:
add_theme_support( 'menus' )
Alternatively, you can register a location (slot) for the menu using register_nav_menu(), which will automatically enable menu support for the theme.
Important: nuance with a missing menu
When using the function, if the specified theme_location does not exist or no menu is assigned to it, WordPress will pass control to the function wp_page_menu(), which outputs a list of all pages instead of the menu. In this case, the parameters container, container_class, and others will start applying to <ul>, not to the container, which may disrupt the expected HTML markup.
Example:
$menu = wp_nav_menu( [ 'theme_location' => 'my_location', 'container' => 'nav', 'container_class' => 'nav header__nav', 'menu_class' => 'header__list', 'menu_id' => 'my-id', 'echo' => false, ] ); echo htmlspecialchars( $menu );
Result when "my_location" is missing or has no menu:
<nav id="my-id" class="header__list"> <ul> <li class="page_item page-item-14813"><a href="URL">Page Title</a></li> </ul> </nav>
Result when "my_location" has a menu:
<nav class="nav header__nav"> <ul id="my-id" class="header__list"> <li id="menu-item-6411" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-6411"> <a href="URL">Menu Item Title</a> </li> </ul> </nav>
🛑 This behavior breaks the markup if you expect that menu_class will be applied to <ul>, not to <nav>.
✅ To avoid such situations:
- Ensure that the required
theme_locationis registered via register_nav_menus(). - Assign at least one menu to the specified
theme_locationslot in the admin panel. - Or explicitly specify
menuby name if you want to avoid fallback to wp_page_menu().
🛑 Always check that the menu exists, especially during layout and styling.
For more information on enabling and adding menus, read in a separate article.
Filters for modifying menu items
-
wp_nav_menu_args - filters the list of parameters for wp_nav_menu().
-
nav_menu_item_args - filters the parameters of an individual menu item.
-
nav_menu_css_class - filters the CSS classes (class attribute) of an individual <li> menu item.
-
nav_menu_item_id - filters the ID attribute of an individual <li> menu item.
-
nav_menu_link_attributes - filters the attributes of the <a> menu item: title, target, rel, href.
-
nav_menu_item_title - filters the anchor text of the menu item link.
-
walker_nav_menu_start_el - filters the HTML code for the start of an individual menu item. Start means the unclosed li tag: <li><a></a>
- nav_menu_submenu_css_class - allows changing the CSS classes added to nested ul elements (menu lists). By default, the class sub-menu is added.
Returns
null|String|false. The function outputs the HTML code for the menu.
Usage template
wp_nav_menu( [ 'theme_location' => '', 'menu' => '', 'container' => 'div', 'container_class' => '', 'container_id' => '', 'menu_class' => 'menu', 'menu_id' => '', 'echo' => true, 'fallback_cb' => 'wp_page_menu', 'before' => '', 'after' => '', 'link_before' => '', 'link_after' => '', 'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>', 'depth' => 0, 'walker' => '', ] );
Usage
<?php wp_nav_menu( $args ); ?>
Arguments for the $args parameter
- theme_location(string)
Identifier for the menu location in the template. The identifier is specified when registering the menu using the function register_nav_menu().
If the
theme_locationparameter is not specified, the menu to be displayed will be selected in the following order:- A menu that matches the ID, slug, or description passed in the menu parameter and if this menu has at least one link (one item).
- Otherwise, the first menu from a non-empty slot for the menu.
- Or, it will output the value returned by the function specified in the fallback_cb parameter. By default, this is the function wp_page_menu().
- If nothing matches, the function will output nothing.
Default: ''
- The menu to be displayed. You can specify:
id,slug, ormenu name.
Default: '' - container(string/false)
- What to wrap the ul tag with. Can be:
divornav.
If you do not want to wrap it with anything, write false:container => false.
Default: div - container_class(string)
- Value of the
class=""attribute for the menu container.
Default: menu-{menu slug}-container - container_id(string)
- Value of the
id=""attribute for the menu container.
Default: '' - container_aria_label(string) (WP 5.5)
- Value of the
aria-label=""attribute for the menu container.
Default: '' - Value of the class attribute for the ul tag.
Default: menu - Value of the id attribute for the ul tag.
Default: menu-{menu slug} - items_wrap(string)
- Template wrapper for menu items. The template must have the placeholder %3$s, the rest is optional.
Default: '<ul id="%1$s" class="%2$s">%3$s</ul>' - fallback_cb(string)
- The function to handle output if no menu is found.
Passes all $args arguments to the specified function.
Set an empty string''or'__return_empty_string'to output nothing if the menu is absent.
Default: wp_page_menu - before(string)
- Text before the
<a>tag in the menu.
Default: '' - after(string)
- Text after each
</a>tag in the menu.
Default: '' - link_before(string)
- Text before the anchor of each link in the menu.
Default: '' - link_after(string)
- Text after the anchor of each link in the menu.
Default: '' - depth(int)
- Up to what level of nesting to show links (menu items). 0 - all levels.
Default: 0 - item_spacing(string) (WP 4.7)
- Whether to keep line breaks in the HTML code of the menu. Can be:
preserveordiscard
Default: 'preserve' - echo(true/false)
- Output to the screen or return for processing.
Default: true - walker(object)
Class that will be used to build the menu. You need to specify an instance of the object, not a string, for example
new My_Menu_Walker().For usage see this example.
Default: Walker_Nav_Menu()
Examples
#1 The first non-empty menu, with default output settings:
<?php wp_nav_menu(); ?>
#2 Adding a word at the beginning of the menu
This example shows how to add a word to the beginning of a menu, in the menu item style (just not a link).
Let's add the word "List" to the beginning of the menu, also let's specify the id attribute to the created li tag:
<?php wp_nav_menu( [ 'theme_location' => 'primary', 'items_wrap' => '<ul><li id="item-id">List: </li>%3$s</ul>' ] ); ?>
#3 Let's display a menu called "Site Navigation":
<?php wp_nav_menu( [ 'menu' => 'Site Navigation' ] ); ?>
#4 Menu from pages. Example from the theme: Twenty Ten.
If no output parameters are specified and the menu is not found, the menu will be built from pages, using the function wp_page_menu().
In this example, the menu attached to the 'primary' menu area will be output:
<div id="access" role="navigation"> <?php wp_nav_menu( [ 'container_class' => 'menu-header', 'theme_location' => 'primary' ] ); ?> </div>
#5 Using the wp_nav_menu_args filter to set defaults for all menus
To remove the container for all navigation menus at once, use the following code in the functions.php theme file. Use the hook wp_nav_menu_args:
add_filter( 'wp_nav_menu_args', 'my_wp_nav_menu_args' );
function my_wp_nav_menu_args( $args = '' ){
$args['container'] = false;
return $args;
} #6 Let's remove the container of only one rendered menu
<?php wp_nav_menu( [ 'container' => '' ] ); ?>
#7 Remove ul wrapper
This example will remove the <ul> tag wrapper from the menu:
<?php wp_nav_menu( [ 'items_wrap' => '%3$s' ] ); ?>
#8 Add CSS classes to all menus
Using the hook we can add our own CSS classes, as long as we fulfill the prerequisite.
Let's add a CSS class if it is a post and the name of the menu item is "blog":
add_filter( 'nav_menu_css_class', 'special_nav_class', 10, 2 );
function special_nav_class( $classes, $item ){
if( is_single() && $item->title == "Blog" ){
$classes[] = "special-class";
}
return $classes;
} #9 Using your function to build a menu
Example of Walker_Nav_Menu class extension, to create your own custom HTML code which is outputs by function wp_nav_menu(). Our HTML code will be written specifically for our theme. Below is the code of its own arbitrary class that builds the menu. It adds menu depth and even/odd CSS classes to menu elements (both ul and li): To avoid reinventing the wheel, we copy the code of the Walker_Nav_Menu{} class and just modify it as we need. Now that the class is ready, use it in wp_nav_menu() function. To do this, specify an instance of our class in the Now use our function wherever you want to display the menu:/**
* Custom walker class for nav menus.
*/
class My_Walker_Nav_Menu extends Walker_Nav_Menu {
/**
* Adds classes to ul sub-menus.
*
* @return void
*/
function start_lvl( &$output, $depth = 0, $args = null ) {
// depth dependent classes
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
$display_depth = ( $depth + 1); // because it counts the first submenu as 0
$classes = [
'sub-menu',
( $display_depth % 2 ? 'menu-odd' : 'menu-even' ),
( $display_depth >= 2 ? 'sub-sub-menu' : '' ),
'menu-depth-' . $display_depth,
];
$class_names = implode( ' ', $classes );
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
}
//
/**
* Adds main classes to li's and links.
*
* @return void
*/
function start_el( &$output, $data_object, $depth, $args, $current_object_id = 0 ) {
$item = $data_object; // use more descriptive name for use within this method.
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
// depth dependent classes
$depth_classes = [
( $depth == 0 ? 'main-menu-item' : 'sub-menu-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) $item->classes;
$class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );
// build html
$output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';
// link attributes
$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 ) .'"' : '';
$attributes .= ' class="menu-link ' . ( $depth > 0 ? 'sub-menu-link' : 'main-menu-link' ) . '"';
$item_output = strtr( '{BEFORE}<a{ATTRIBUTES}>{LINK_BEFORE}{TITLE}{LINK_AFTER}</a>{AFTER}', [
'{BEFORE}' => $args->before,
'{ATTRIBUTES}' => $attributes,
'{LINK_BEFORE}' => $args->link_before,
'{TITLE}' => apply_filters( 'the_title', $item->title, $item->ID ),
'{LINK_AFTER}' => $args->link_after,
'{AFTER}' => $args->after,
] );
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
walker parameter.function my_nav_menu( $args ) {
$args = array_merge( [
'container' => 'div',
'container_id' => 'top-navigation-primary',
'container_class' => 'top-navigation',
'menu_class' => 'menu main-menu menu-depth-0 menu-even',
'echo' => false,
'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>',
'depth' => 10,
'walker' => new My_Walker_Nav_Menu()
], $args );
echo wp_nav_menu( $args );
}
my_nav_menu( [
'theme_location' => 'navigation_menu_primary'
] );
#10 Separate menus for (un)authorized users
If we want to show different menus, for authorized and unauthorized users, we use the conditional tag is_user_logged_in():
If ( is_user_logged_in() ) {
wp_nav_menu( array( 'theme_location' => 'logged-in-menu' ) );
}
else {
wp_nav_menu( array( 'theme_location' => 'logged-out-menu' ) );
}
You need to create 2 different menus in the admin and attach them to their respective locations (areas).
#11 CSS class for parent menu items
If you want to add CSS class for menu elements, which have children (stacked lists of links), then do as follows
add_filter( 'wp_nav_menu_objects', 'css_for_nav_parrent' );
function css_for_nav_parrent( $items ){
foreach( $items as $item ){
if( __nav_hasSub( $item->ID, $items ) ){
// all elements of the 'classes' field of the menu, will be combined and rendered in the class attribute of the HTML tag <li>
$item->classes[] = 'menu-parent-item';
}
}
return $items;
}
function __nav_hasSub( $item_id, $items ){
foreach( $items as $item ){
if( $item->menu_item_parent && $item->menu_item_parent == $item_id )
return true;
}
return false;
} #12 Adding a class to individual menu items
There is a special hook for this: nav_menu_css_class. Classes can be added or removed through it. For an example, let's add the my__class class to all menu items:
add_filter( 'nav_menu_css_class', 'add_my_class_to_nav_menu', 10, 2 );
function add_my_class_to_nav_menu( $classes, $item ){
/* $classes contains
Array(
[1] => menu-item
[2] => menu-item-type-post_type
[3] => menu-item-object-page
[4] => menu-item-284
)
*/
$classes[] = 'my__class';
return $classes;
} #13 Display the menu only if it exists
By default, if there is no menu, it will display the pages of the site instead. But if you want to display the menu only if it's created in the admin panel, specify the fallback_cb parameter as __return_empty_string:
wp_nav_menu( [ 'theme_location' => 'primary-menu', 'fallback_cb' => '__return_empty_string ] );
#14 Output menu sub-item only
Let's say we have a first level and each of the first level items has its own submenu. We need to display such a submenu for an item with class menu-item-135:
## Cut all LI of the desired submenu and output them in our UL block
$menu = wp_nav_menu( [
'theme_location' => 'header_menu',
'container' => '',
'echo' => 0,
] );
$regex_part = preg_quote( 'menu-item-135' );
// print the "gotovye-resheniya" submenu
preg_match('~'. $regex_part .'.*sub-menu[^>]+>(.*?)</ul>~s', $menu, $mm );
if( ! empty( $mm[1] ) )
echo "<ul>$mm[1]</ul>"
It's not very optimal, but it works. It may come in handy sometimes, for little-visited sites where you need to get a quick result.
#15 Caching menus (wp_nav_menu) into the object cache
The code below demonstrates how to cache the entire menu code in object cache. The cache will be flushed when the menu is updated. Caching will work for all menus.<?php
Pj_Cached_Nav_Menus::load();
/**
* Caches calls to wp_nav_menu().
*/
class Pj_Cached_Nav_Menus {
public static $ttl = 3600; // use 0 to cache forever (until nav menu update)
public static $cache_menus = array();
public static function load() {
add_filter( 'pre_wp_nav_menu', array( __CLASS__, 'pre_wp_nav_menu' ), 10, 2 );
add_filter( 'wp_nav_menu', array( __CLASS__, 'maybe_cache_nav_menu' ), 10, 2 );
add_action( 'wp_update_nav_menu', array( __CLASS__, 'clear_caches' ) );
}
private static function _cache_key( $args ) {
$_args = (array) $args;
unset( $_args['menu'] );
return 'pj-cached-nav-menu:' . md5( json_encode( $_args ) );
}
private static function _timestamp() {
static $timestamp;
if ( ! isset( $timestamp ) )
$timestamp = get_option( 'pj-cached-nav-menus-timestamp', 0 );
return $timestamp;
}
public static function pre_wp_nav_menu( $output, $args ) {
if ( ! empty( $args->menu ) )
return $output;
$cache_key = self::_cache_key( $args );
self::$cache_menus[] = $cache_key;
$cache = get_transient( $cache_key );
if ( is_array( $cache ) && $cache['timestamp'] >= self::_timestamp() ) {
$output = $cache['html'] . '<!-- pj-cached-nav-menu -->';
}
return $output;
}
public static function maybe_cache_nav_menu( $html, $args ) {
$cache_key = self::_cache_key( $args );
if ( ! in_array( $cache_key, self::$cache_menus ) )
return $html;
$cache = array(
'html' => $html,
'timestamp' => time(),
);
set_transient( $cache_key, $cache, self::$ttl );
return $html;
}
public static function clear_caches() {
update_option( 'pj-cached-nav-menus-timestamp', time() );
}
}
Original code: https://github.com/pressjitsu/cached-nav-menus
Composer package: https://github.com/inpsyde/menu-cache
Another similar example: https://www.bjornjohansen.com/wordpress-menu-cache
CSS classes for menu items
See the added classes in the function _wp_menu_item_classes_by_context().
The following CSS classes are added to menu items (divided by conditions on which pages the user is located):
For all items on all pages
-
.menu-item— for all menu items; -
.menu-item-object-{object}— for all items. {object} will be replaced with the name of the post type or taxonomy:.menu-item-object-category— for categories..menu-item-object-tag— for tags..menu-item-object-page— for static pages..menu-item-object-custom‒ for custom menu items.
.menu-item-type-{type}— for all menu items. {type} will be replaced with the type of link (post or taxonomy):.menu-item-type-post_type— static page, custom post type..menu-item-type-taxonomy— category, tag, or custom taxonomy..menu-item-type-custom‒ for custom menu items.
On the current page
.current-menu-item— if the link in the menu matches the address of the viewed page. The current page.
For parent items of the viewed page
.current-menu-parent.current-{taxonomy}-ancestor.current-{post_type}-ancestor
For items somehow related to the viewed page
.current-menu-ancestor.current-{object}-ancestor.current-{type}-ancestor
For items related to the homepage of the site
.menu-item-home
Compatibility with the function wp_page_menu()
.page_item.page-item-{$menu_item->object_id}.current_page_item.current_page_parent.current_page_ancestor
Others
.menu-item-has-children‒ If the item has child elements.ul.sub-menu‒ For the child element ul..menu-item-privacy-policy‒ menu item "Privacy Policy Page"..menu-item-home‒ menu item "Home Page".
The $item object
$item parameters
In examples, the menu item $item is often used. Below are shown almost all parameters of this item:
| Field | Description |
|---|---|
| ID | ID of the menu item |
| menu_item_parent | ID of the parent menu item |
| classes | array of classes for the menu item |
| post_date | date of addition |
| post_modified | date of last modification |
| post_author | ID of the user who added this menu item |
| title | title of the menu item |
| url | link of the menu item |
| attr_title | title attribute of the link |
| xfn | rel attribute of the link |
| target | target attribute of the link |
| current | equals 1 if this is the current item |
| current_item_ancestor | 1 if the current item is a child item |
| current_item_parent | 1 if the current item is a parent item |
| menu_order | order number in the menu |
| object_id | ID of the menu object. Post, term, etc. |
| type | type of the menu object (taxonomy, post) |
| object | name of the taxonomy, post type: page, category, post_tag ... |
| type_label | localized name of the type: Category, Page |
| post_parent | ID of the parent post |
| post_title | title of the post |
| post_name | slug of the post |
Example of the $item object
WP_Post Object ( [ID] => 10 [post_author] => 5 [post_date] => 2019-02-11 13:33:39 [post_date_gmt] => 2019-02-11 13:33:39 [post_content] => [post_title] => New [post_excerpt] => [post_status] => publish [comment_status] => closed [ping_status] => closed [post_password] => [post_name] => new [to_ping] => [pinged] => [post_modified] => 2019-02-11 23:10:19 [post_modified_gmt] => 2019-02-11 23:10:19 [post_content_filtered] => [post_parent] => 0 [guid] => http://dh5.com/?p=10 [menu_order] => 1 [post_type] => nav_menu_item [post_mime_type] => [comment_count] => 0 [filter] => raw [db_id] => 10 [menu_item_parent] => 0 [object_id] => 10 [object] => custom [type] => custom [type_label] => Custom Link [title] => New [url] => # [target] => [attr_title] => [description] => [classes] => Array [0] => extra-sub-menu [1] => menu-item [2] => menu-item-type-custom [3] => menu-item-object-custom [xfn] => [current] => [current_item_ancestor] => [current_item_parent] => )
Example of using the walker parameter
In the walker, you can specify an object that will build the menu. In this object, you can describe the HTML code of the resulting menu.
If you need to create a menu for a non-standard layout, it is sometimes easier to modify this object than to redo the layout.
As an example of the walker object, we take the class Walker_Nav_Menu{}, which is used by default. We are only interested in one method start_el(). It is responsible for the HTML of each item. Usually, it is enough to change only this. To do this, you need to create your own class that will extend the Walker_Nav_Menu class and specify it in the walker parameter when calling the menu.
Let's look at an example. The code of the start_el() method is taken without changes. We use it as a template:
class My_Walker_Nav_Menu extends Walker_Nav_Menu {
/**
* Starts the element output.
*
* @since 3.0.0
* @since 4.4.0 The {@see 'nav_menu_item_args'} filter was added.
*
* @see Walker::start_el()
*
* @param string $output Passed by reference. Used to append additional content.
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $id Current item ID.
*/
public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
$t = '';
$n = '';
} else {
$t = "\t";
$n = "\n";
}
$indent = ( $depth ) ? str_repeat( $t, $depth ) : '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;
$args = apply_filters( 'nav_menu_item_args', $args, $item, $depth );
$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 ) . '"' : '';
// create the HTML code for the menu item
$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 );
$title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
$item_output .= $args->link_before . $title . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
Now, when calling the menu, we specify our walker:
wp_nav_menu( array( 'theme_location' => 'head_menu', 'walker' => new My_Walker_Nav_Menu(), ) );
Done, now each menu item will be built according to our desired HTML scheme.
BEM menu using filters
The layout will be formed according to the BEM methodology:
<ul class="menu menu--main menu--horizontal"> <li class="menu-node menu-node--main_lvl_1 menu-node--active"> <a href="#" class="menu-link menu-link--active">Item 1</a> <ul class="menu menu--dropdown menu--vertical"> <li class="menu-node menu-node--main_lvl_2"> <a href="#" class="menu-link">Subitem 1.1</a> </li> <li class="menu-node menu-node--main_lvl_2"> <a href="#" class="menu-link">Subitem 1.2</a> </li> </ul> </li> <li class="menu-node menu-node--main_lvl_1"> <a href="#" class="menu-link">Item 2</a> </li> <li class="menu-node menu-node--main_lvl_1"> <a href="#" class="menu-link">Item 3</a> </li> </ul>
File index.php or another for outputting the menu
<?php wp_nav_menu( [ 'theme_location' => 'header-menu', ] );
File functions.php
<?php
add_action( 'after_setup_theme', function () {
register_nav_menus( [
'header-menu' => 'Top Area',
'footer-menu' => 'Bottom Area',
] );
} );
// Changes the main menu parameters
add_filter( 'wp_nav_menu_args', 'filter_wp_menu_args' );
// Changes the id attribute of the li tag
add_filter( 'nav_menu_item_id', 'filter_menu_item_css_id', 10, 4 );
// Changes the class attribute of the li tag
add_filter( 'nav_menu_css_class', 'filter_nav_menu_css_classes', 10, 4 );
// Changes the class of the nested ul
add_filter( 'nav_menu_submenu_css_class', 'filter_nav_menu_submenu_css_class', 10, 3 );
// Adds classes to links
add_filter( 'nav_menu_link_attributes', 'filter_nav_menu_link_attributes', 10, 4 );
function filter_wp_menu_args( $args ) {
if ( $args['theme_location'] === 'header-menu' ) {
$args['container'] = false;
$args['items_wrap'] = '<ul class="%2$s">%3$s</ul>';
$args['menu_class'] = 'menu menu--main menu-horizontal';
}
return $args;
}
function filter_menu_item_css_id( $menu_id, $item, $args, $depth ) {
return $args->theme_location === 'header-menu' ? '' : $menu_id;
}
function filter_nav_menu_css_classes( $classes, $item, $args, $depth ) {
if ( $args->theme_location === 'header-menu' ) {
$classes = [
'menu-node',
'menu-node--main_lvl_' . ( $depth + 1 )
];
if ( $item->current ) {
$classes[] = 'menu-node--active';
}
}
return $classes;
}
function filter_nav_menu_submenu_css_class( $classes, $args, $depth ) {
if ( $args->theme_location === 'header-menu' ) {
$classes = [
'menu',
'menu--dropdown',
'menu--vertical'
];
}
return $classes;
}
function filter_nav_menu_link_attributes( $atts, $item, $args, $depth ) {
if ( $args->theme_location === 'header-menu' ) {
$atts['class'] = 'menu-link';
if ( $item->current ) {
$atts['class'] .= ' menu-link--active';
}
}
return $atts;
}Changelog
| Since 3.0.0 | Introduced. |
| Since 4.7.0 | Added the item_spacing argument. |
| Since 5.5.0 | Added the container_aria_label argument. |