jQuery HTML5 :dataAttr Pseudo Selector
1 min read

Problem

A while back someone on twitter was asking me how they might find a set of DOM elements by using doing a partial search on their HTML5 data attribute.

I'm not actually sure what type of use case you would need for such problem, but I thought it was an interesting issue to work on, so I went ahead and took a stab at solving it.

<!--
Goal: To match a set of elements based on their HTML5 data attribute.
The match can be an exact search, starts with, ends with, or contains.
Example: Find elements that HTML5 data attribute starts with "my".
-->
<p>Hello World</p> <!-- don't match -->
<p data-m>Hello World</p> <!-- don't match -->
<p data-my="1">Hello World</p> <!-- match -->
<p data-myt="11">Hello World</p> <!-- match -->
<p data-myty="111">Hello World</p> <!-- match -->
<p data-mytype="1111">Hello World</p> <!-- match -->
<p data-mytyped="11111">Hello World</p> <!-- match -->
view raw fiddle.html hosted with ❤ by GitHub

Desired Solution

In order to solve the above example of finding elements that start with a certain HTML5 data attribute, I wanted to follow a similar API to that of jQuery Attribute Selectors with the ^=, \$=, etc... syntax. The following is an example of how I thought the solution should look like.

$( "p:dataAttr(my, ^=)" ).addClass( "found" );
view raw selector.js hosted with ❤ by GitHub

Custom Pseudo Selector

In order to create an API like the above I needed to create a custom pseudo selector, much like what you've seen when using :last, :odd, :eq( number ), and numerous other common selectors.

/* by Elijah Manor
* Filter results by HTML5 data attributes either at design-time
* ( HTML5 data-attr ) or at runtime ( internal jQuery data() )
*
* Usages
* $( "p:dataAttr(mytype)" ) // p's with data mytype attr
* $( "p:dataAttr(my, ^=)" ) // p's that start with data myty attr
* $( "p:dataAttr(type, $=)" ) // p's that ends with data myty attr
* $( "p:dataAttr(yty, ~=)" ) // p's that contains data myty attr
*/
$.expr[ ":" ].dataAttr = function( element, index, match, array ) {
var parameters = match[ 3 ].split( "," ),
attr = $.trim( parameters[0] ),
compare = $.trim( parameters[1] ) || "=",
data = $( element ).data(),
matches = false;
$.each( data, function( key, _ ) {
index = key.indexOf( attr );
if ( compare === "=" ) {
matches = key === attr;
} else if ( compare === "^=" ) {
matches = index === 0;
} else if ( compare === "$=" ) {
matches = ~index && index === ( key.length - attr.length );
} else if ( compare === "~=" ) {
matches = !!~index;
}
if ( matches ) { return false; }
});
return matches;
};
view raw fiddle.js hosted with ❤ by GitHub

Unit Tests

I didn't want to just have some code laying around that wasn't thoroughly tested, so I went ahead and created a set of unit tests to cover various scenarios. I could have kept going, but I thought the following was a decent set of tests to start with.

Edit post on GitHub


QUnit Composite Addon: Running Multiple jQuery Test Files
Tweet Package for Sublime Text 2