Opened 10 years ago
Last modified 9 years ago
#35127 new enhancement
Allow wp_nav_menu() function to add container attributes, including Schema.org structured data
| Reported by: |
|
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
- First, we just need to register
container_attsas a valid argument in line 257 by adding it to the$defaultsarray
$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() );
- Next, we process and concatenate the attributes and add them to the container via the
$nav_menustring 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!
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 );