1
\$\begingroup\$

With jQuery, I'd like to be able to easily select the following types of elements:

  • Elements that have a specific value set for a specific data attribute in the raw HTML
  • Elements that have had a specific value set, or changed, for a specific data attribute via jQuery
    • If, the value was initially set in the HTML but then changed by jQuery, the new value should be tested against
  • Elements that do not have a specific value set for a specific data attribute

I have written this and tested it as well as I know how. Everything seems to work well.


Concerns:

Note that I am converting all values to strings for comparison. I did this because I ran into issues like:

  • check-value="true" is read by jQuery as (bool) true but ':data(check-value,true)' evaluates to (string) 'true' when I expected these to match
  • check-value="22" is read by jQuery as (int) 22 but ':data(check-value,22)' evaluates to (string) '22' when I expected these to match

For those reasons, it just seems easier to work with just strings.

Will this decision bite somewhere I haven't thought of? Can/should I improve the efficiency of this code?


My code (and a jsFiddle)

/**
 * Custom expression that filters jQuery internal data
 * Inspired by http://stackoverflow.com/a/13276132/1376624 
 * @param dom node elem the currently iterated dom element from the matched collection
 * @param int index the index of the currently iterated dom element in the matched collection
 * @return array m   // not exactly sure what this is but it's passed as an array like `["data", "data", "", "check-value,11"]` making m[3] the value I need
 */
$.expr[':'].data = function (elem, index, m) {
    m = m[3].split(',');
    var key = m[0];
    var val = m[1] || null;
    var data = $(elem).data(key);
    data = data === false ? 'false' : data === true ? 'true' : data ? data.toString() : data; // convert booleans and numbers to strings to make things simpler
    return val ? data === val : !! data; // if a value was supplied, 
};


// set some values dynamically for the test
var vals = [11, 'Test', true, false, 'true', 'false'];
$('.dynamic').each(function (i, e) {
    $(e).data('check-value', vals[i]);
});
$('.modified').data('check-value', 1);

// Test possible use cases

// match all elements that have the provided values set as data attribute in the raw HTML
$('.static:data(check-value,11)').addClass('highlight');
$('.static:data(check-value,Test)').addClass('highlight');
$('.static:data(check-value,true)').addClass('highlight');
$('.static:data(check-value,false)').addClass('highlight');

// match all elements that have had the provided values set as data attribute via jQuery
$('.dynamic:data(check-value,11)').addClass('highlight');
$('.dynamic:data(check-value,Test)').addClass('highlight');
$('.dynamic:data(check-value,true)').addClass('highlight');
$('.dynamic:data(check-value,false)').addClass('highlight');

// match elements that have any value set as data attribute 
$('.exists:data(check-value)').addClass('highlight');

// match elements that DO NOT have a value set as data attribute 
$('.exists:not(:data(check-value))').addClass('highlight');

// match elements that initially set in the HTML but then changed by jQuery
$('.modified:data(check-value,1)').addClass('highlight');

// make sure original value from raw html no longer matches, this one should fail
$('.modified:data(check-value,0)').addClass('bad');
.highlight {
    background-color:yellow;
}
.bad {
    background-color:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h4>Data in HTML</h4>
<div class="test static" data-check-value="11">11</div>
<div class="test static" data-check-value="Test">Test</div>
<div class="test static" data-check-value="true">true</div>
<div class="test static" data-check-value="false">false</div>
<hr>

<h4>Dynamically set</h4>
<div class="test dynamic" data-check-value="">11</div>
<div class="test dynamic" data-check-value="">Test</div>
<div class="test dynamic" data-check-value="">true -bool</div>
<div class="test dynamic" data-check-value="">false -bool</div>
<div class="test dynamic" data-check-value="">true -string</div>
<div class="test dynamic" data-check-value="">false -string</div>
<hr>

<h4>Match data attribute value exists</h4>
<div class="test exists" data-check-value="some value">Random value</div>
<hr>

<h4>Match data attribute value doesnt exist</h4>
<div class="test exists" data-check-value="">No value</div>
<div class="test exists">Not present at all on element</div>
<hr>

<h4>Match new data attribute value changed via jQuery</h4>
<div class="test modified" data-check-value="0">Modified value</div>

\$\endgroup\$

Your Answer

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

Browse other questions tagged or ask your own question.