1

I want to add a span to top level links of a wordpress menu called "main".

The desired html output (wordpress classes removed for a clear visual) would look like so:

    <ul>
    <li><a href="#"><span>Link</span></a></li>
    <li><a href="#"><span>Link</span></a>
        <ul>
            <li><a href="#">Sub Link (no span)</a></li>
            <li><a href="#">Sub Link (no span)</a></li>
            <li><a href="#">Sub Link (no span)</a></li>
        </ul>
    </li>
    <li><a href="#"><span>Link</span></a></li>
    <li><a href="#"><span>Link</span></a></li>
    </ul>
    

I found this wordpress filter some time ago when Googling (most likely stack overflow, can't remember where) that adds the span nicely, but it adds it to every menu in my theme.

add_filter( 'wp_nav_menu_objects', function( $items ) {
      foreach ( $items as $item ) {
        if (!$item->menu_item_parent) {
           $item->title = '<span>' . $item->title . '</span>';
        }
    }
    return $items;
});

Q1. Is there a way to add this filter just to the menu called main?

Q2. Is that the best filter / way to achieve this?

Thanks for any help.

1 Answer 1

1

$args, the second parameter passed to wp_nav_menu_objects can be used to check which menu is being processed. This will allow us to target only the desired menu.

In the code below, we check if the slug of the menu is main and if so, wrap the top level menu items in span tags.

/**
 * Filters the sorted list of menu item objects before generating the menu's HTML.
 *
 * @param array    $sorted_menu_items The menu items, sorted by each menu item's menu order.
 * @param stdClass $args              An object containing wp_nav_menu() arguments.
 */
add_filter( 'wp_nav_menu_objects', 'wpse_wp_nav_menu_objects', 10, 2 );
function wpse_wp_nav_menu_objects( $sorted_menu_items, $args  ) {
    // Only modify the "main" menu.
    if ( ! isset( $args->menu->slug ) || 'main' !== $args->menu->slug ) {
        return $sorted_menu_items;
    }

    // Loop over the menu items wrapping only top level items in span tags.
    foreach ( $sorted_menu_items as $item ) {
        if ( ! $item->menu_item_parent ) {
            $item->title = '<span>' . $item->title . '</span>';
        }
    }

    return $sorted_menu_items;
}

The filter above will technically run for all of the menus, but with our check in place, it will only affect the desired menu.

Whether or not wp_nav_menu_objects is the best filter is up for debate, but I think it's a good fit to solve the issue at hand. It's certainly better than using one of the later filters such as wp_nav_menu_items, wp_nav_menu_{$menu->slug}_items, or wp_nav_menu which would require parsing the menu's HTML.

2
  • 1
    HI and thankyou Dave, That works perfectly. Thanks for the clear commenting and answer. Well done and thanks so much Jun 28, 2017 at 4:02
  • You got it, @user3643520! Jun 28, 2017 at 4:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.