April 17, 2021

Fix Oxygen repeater duplicate IDs

A code snippet to give all elements inside the Oxygen Repeater a unique ID
Home / Post / Fix Oxygen repeater duplicate IDs

Description

When using the Repeater element in Oxygen Builder, it will assign duplicate id attribute values to all child elements.

This is a violation of the WCAG 4.1.1, which states that attributes must be unique. There are good reasons for this. Most importantly, duplicate ids can cause problems for assistive technologies such as screen readers.

This bug was reported years ago, but Soflyy didn't provide a fix yet. There are at least two different third-party solutions to date. Both of them will remove the id attribute entirely, which may not be the solution you want.

This code snippet (download link and source code at the bottom of this page) uses a proper XML parser to filter the HTML output from the builder and give each node inside the repeater an incremental id based on the original id attribute.

So, instead of this:

<div id="list" class="oxy-dynamic-list">
	<article id="item">
		<h2 id="headline">
			First Headline
		</h2>
		<p id="paragraph">
			First paragraph.
		</p>
	</article>
	<article id="item">
		<h2 id="headline">
			Second Headline
		</h2>
		<p id="paragraph">
			Second paragraph.
		</p>
	</article>
</div>

You will get this:

<div id="list" class="oxy-dynamic-list">
	<article id="item-1">
		<h2 id="headline-1">
			First Headline
		</h2>
		<p id="paragraph-1">
			First paragraph.
		</p>
	</article>
	<article id="item-2">
		<h2 id="headline-2">
			Second Headline
		</h2>
		<p id="paragraph-2">
			Second paragraph.
		</p>
	</article>
</div>

Please note that any CSS styling you might have applied to the original element's ID selector no longer will apply. Copy it to a class instead.

Download

The download is a JSON file for use with the Code Snippets or Advanced Scripts plugin.

Download Code Snippet

File: oxygen-repeater-incremental-ids-1.0.3.code-snippets.json.zip
Version: 1.0.3 (2021-04-18)


Source Code

add_filter( 'do_shortcode_tag', function ( $output, $tag, $attr ) { 
	
	if ( in_array($tag, ['oxy_dynamic_list_3']) ) {
		$query = '//*[@id]/*';
		$output = cl_rename_element_id($query, $output);
	}
	
	return $output;
	
}, 10, 3);

function cl_rename_element_id($query, $output) {
	
	$flag = libxml_use_internal_errors(true);
	
	$dom = new DOMDocument();
	$dom->loadHTML(mb_convert_encoding( $output, 'HTML-ENTITIES', 'UTF-8') );
	$xpath = new DOMXPath($dom);
	$elements = $xpath->query($query);
	
	if ( $elements->length > 0 ) {
		
		$counter = 1;
		$attribute = 'id';
		$repeater_id = $elements[0]->parentNode->getAttribute($attribute);
	
		foreach( $elements as $element ) {
		
			$parent_id = $element->parentNode->getAttribute($attribute);
		
			if ( $element->hasAttribute($attribute) ) {
				$new_id = $element->getAttribute($attribute) . '-' . ( ( $parent_id == $repeater_id ) ? $counter : ( $counter - 1 ) );
				$element->removeAttribute($attribute);
	    		$element->setAttribute($attribute, $new_id);
			}
		
			if ( $parent_id == $repeater_id ) $counter++;
		}
	
		$output = $dom->saveHTML();
		
	}
	
	libxml_clear_errors();
	libxml_use_internal_errors($flag);
	
	return $output;
}

More Posts
crossmenu linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram