WordPress.org

Make WordPress Core

Opened 3 years ago

Last modified 2 years ago

#35127 new enhancement

Allow wp_nav_menu() function to add container attributes, including Schema.org structured data

Reported by: sevenspark Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 4.4
Component: Menus Keywords:
Focuses: Cc:

Description

I'd like to propose an enhancement that would improve the flexibility of the wp_nav_menu() function to allow additional attributes to be added to the <nav> container of menus. The main purpose of this proposal is to enable the ability to add schema.org structured data attributes to the menu, but this could be extended to other purposes as well given its flexibility.

Currently, wp_nav_menu() offers 3 container-related arguments: $container, $container_id, and $container_class. Using these arguments, we can create container elements such as

<nav id="main-menu" class="my-menu">

However, it is not possible to create elements with Structured Data such as the SiteNavigationElement as recommended by Google, which would look like this

<nav id="main-menu" class="my-menu" role="navigation" itemscope="itemscope" itemtype="http://schema.org/SiteNavigationElement">

Adding the role, itemscope, and itemtype attributes is currently impossible with wp_nav_menu() (without resorting to error-prone string replacement), so theme authors and end users need to resort to hard-coding the menu wrapper in order to add such attributes.

My proposal is to add a new container_atts argument to wp_nav_menu(), which will allow arbitrary attributes to be added to the container when the $container argument is defined.

The changes required are very minimal and only apply to the wp_nav_menu() function in wp-includes/nav-menu-template.php

  1. First, we just need to register container_atts as a valid argument in line 257 by adding it to the $defaults array
$defaults = array( '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' => '', 'theme_location' => '' , 'container_atts' => array() );
  1. Next, we process and concatenate the attributes and add them to the container via the $nav_menu string on line 355
$atts = '';  //initialize string to concatenate attributes
foreach( $args->container_atts as $att => $att_val ){  //iterate over each attribute
	$atts.= ' '.$att;  //add attribute name, spaced out

	//Add attribute value, if it exists.  This logic allows for attributes without values
	if( ! empty( $att_val ) ) $atts.= '="'.esc_attr( $att_val ).'"';
}
$nav_menu .= '<'. $args->container . $id . $class . $atts . '>';   //add the string of attributes to the nav/div container

That's all the code that would be required to enable this capability. This then allows for function calls such as this:

<?php
wp_nav_menu( array(
  'theme_location'  => 'primary',
  'container'       => 'nav',
  'container_class' => 'my-menu',
  'container_atts'  => array(
    'role'      => 'navigation',
    'itemscope' => 'itemscope',
    'itemtype'  => 'http://schema.org/SiteNavigationElement',
  )
));

Which would produce this output for the container element:

<nav class="my-menu" role="navigation" itemscope="itemscope" itemtype="http://schema.org/SiteNavigationElement">

(The other thing I might consider is calling unset() on the id and class keys in the $container_atts array, to prevent users from adding those attributes, since there are already dedicated arguments for them. But that is likely adding more restriction than necessary.)

I think this would be a really simple change, and add a lot of flexibility to the wp_nav_menu() function. Beyond just adding valuable attributes like structured data, adding data-attributes could be useful for javascript-driven applications, and I'm sure there are plenty of other possibilities I haven't thought of.

Thanks for your consideration!

Change History (2)

#1 @yipeecaiey
3 years ago

For anyone looking to incorporate schema.org attributes into the nav, you can accomplish this using the 'wp_nav_menu' filter.
Here's a quick function that you can copy and modify as needed:

function filter_wp_nav_menu($nav_menu,$args) {
    global $schemaURL;

    //map out the nav_menu for parsing
    $dom = new DOMDocument();
    @$dom->loadHTML($nav_menu);
    $x = new DOMXPath($dom);

    //parse the <a> nodes
    foreach($x->query("//a") as $node) {
        $node->setAttribute("itemprop","url");
    }

    //parse the <li> nodes
    foreach($x->query("//li") as $node) {
        //$node->setAttribute("itemsomething","xxxx");
    }

    //parse the <ul> nodes
    foreach($x->query("//ul") as $node) {
        //$node->setAttribute("itemsomething","xxxx");
    }

    //parse the <nav> nodes
    foreach($x->query("//nav") as $node) {
        $node->setAttribute("itemscope", "itemscope");
        $node->setAttribute("itemtype", $schemaURL.'SiteNavigationElement');
    }

    //regenerate the html
    //NOTE: this assumes only one nav node. Multiple nav nodes will break this filter
    $nav_menu = $node->c14n();

    return $nav_menu;

}
add_filter( 'wp_nav_menu', 'filter_wp_nav_menu', 10, 2 );

#2 @whatsupmf
2 years ago

I think this would be a valuable enhancement because it would improve accessibility (for HTML5 and non-HTML5 user agents alike) by easily permitting the insertion of the role="navigation" attribute.

Last edited 2 years ago by whatsupmf (previous) (diff)
Note: See TracTickets for help on using tickets.