1184

Is there an easy and straight-forward method to select elements based on their data attribute? For example, select all anchors that has data attribute named customerID which has value of 22.

I am kind of hesitant to use rel or other attributes to store such information, but I find it much harder to select an element based on what data is stored in it.

Qwerty
  • 29,062
  • 22
  • 108
  • 136
Hazem Salama
  • 14,891
  • 5
  • 27
  • 31
  • 2
    See also http://stackoverflow.com/q/4191386/292060 – goodeye Jan 07 '15 at 04:47
  • 1
    That's what helped me to select **all** data attributes (regardless the value): `$('*[data-customerID]')` You can use it with e.g. `$('*[data-customerID]').each( function() { ... });` – Avatar Jan 14 '20 at 08:15
  • @Avatar or you could just use `$('[data-customerID]')` – Jacques Feb 10 '23 at 22:24

12 Answers12

1667
$('*[data-customerID="22"]');

You should be able to omit the *, but if I recall correctly, depending on which jQuery version you’re using, this might give faulty results.

Note that for compatibility with the Selectors API (document.querySelector{,all}), the quotes around the attribute value (22) may not be omitted in this case.

Also, if you work with data attributes a lot in your jQuery scripts, you might want to consider using the HTML5 custom data attributes plugin. This allows you to write even more readable code by using .dataAttr('foo'), and results in a smaller file size after minification (compared to using .attr('data-foo')).

Salvatorelab
  • 11,614
  • 6
  • 53
  • 80
Mathias Bynens
  • 144,855
  • 52
  • 216
  • 248
  • 74
    Just a note that .data('foo') works to get the value of a 'data-foo' attribute since jQuery 1.4.3. Also, since jQuery 1.6: .data('fooBar') gets attribute 'data-foo-bar'. – James McCormack Jul 01 '11 at 14:49
  • 62
    Also if you are interested only in presence of specific data attribute, you could do this: `$('[data-customerID]')` – Darkside Dec 01 '13 at 16:12
  • 13
    This doesn't work, if the data field was set by jquery (using `.data()`), right? – Martin R. May 14 '17 at 11:31
426

For people Googling and want more general rules about selecting with data-attributes:

$("[data-test]") will select any element that merely has the data attribute (no matter the value of the attribute). Including:

<div data-test=value>attributes with values</div>
<div data-test>attributes without values</div>

$('[data-test~="foo"]') will select any element where the data attribute contains foo but doesn't have to be exact, such as:

<div data-test="foo">Exact Matches</div>
<div data-test="this has the word foo">Where the Attribute merely contains "foo"</div>

$('[data-test="the_exact_value"]') will select any element where the data attribute exact value is the_exact_value, for example:

<div data-test="the_exact_value">Exact Matches</div>

but not

<div data-test="the_exact_value foo">This won't match</div>
JTG
  • 8,587
  • 6
  • 31
  • 38
  • 29
    Good. Note that `~=` matches whitespace-separated words whereas `*=` matches any substring. – sam Apr 16 '16 at 02:50
151

Using $('[data-whatever="myvalue"]') will select anything with html attributes, but in newer jQueries it seems that if you use $(...).data(...) to attach data, it uses some magic browser thingy and does not affect the html, therefore is not discovered by .find as indicated in the previous answer.

Verify (tested with 1.7.2+) (also see fiddle): (updated to be more complete)

var $container = $('<div><div id="item1"/><div id="item2"/></div>');

// add html attribute
var $item1 = $('#item1').attr('data-generated', true);

// add as data
var $item2 = $('#item2').data('generated', true);

// create item, add data attribute via jquery
var $item3 = $('<div />', {id: 'item3', data: { generated: 'true' }, text: 'Item 3' });
$container.append($item3);

// create item, "manually" add data attribute
var $item4 = $('<div id="item4" data-generated="true">Item 4</div>');
$container.append($item4);

// only returns $item1 and $item4
var $result = $container.find('[data-generated="true"]');
Community
  • 1
  • 1
drzaus
  • 24,171
  • 16
  • 142
  • 201
  • 1
    aha - turns out someone else points this out at http://stackoverflow.com/questions/4191386/jquery-how-to-find-an-element-based-on-a-data-attribute-value – drzaus Jul 26 '12 at 16:03
  • 4
    and offers a solution with `.filter` [here](http://stackoverflow.com/a/7344459/1037948) – drzaus Jul 26 '12 at 16:09
  • 23
    **it uses some magic browser thingy and does not affect the html**: there is no such thing as magic ;) http://www.learningjquery.com/2011/09/using-jquerys-data-apis – Tomas Ramirez Sarduy Nov 22 '12 at 21:09
  • 86
    @TomSarduy: _["Any sufficiently advanced technology is indistinguishable from magic."](http://en.wikipedia.org/wiki/Clarke's_three_laws)_ – drzaus Jan 13 '13 at 17:23
  • It might not have been apparent from my answer, but it's worth noting that if you change the value with `.data(...)`, it does not *change* the html `data-` attribute, so if you did the following -- **[** 1) `` 2) `$('[data-icon="star"]).data('icon', 'circle')` **]** -- performing the filter `$('[data-icon="star"]')` again would find the original element. – drzaus Mar 10 '13 at 04:53
  • 2
    If you are adding a data attribute that you need to find later, use `$item.attr('data-id', 10);` – Pedro Moreira Apr 08 '14 at 09:16
  • @PedroMoreira - unless I'm misreading your reply, you missed the point of my answer -- you _can't_ find it later if you set it via `.data`. You could set it via `attr`, but then it's not really "data" and can only be used for DOM traversal. Compare the effects of `$('#comment-21628443').data('zaus-reply', 'yes')` and `$('#comment-21628443').attr('data-zaus-reply', 'no')` -- `$('#comment-21628443').data('zaus-reply')` will yield 'yes' but `$('[data-zaus-reply="yes"')` can't be found. – drzaus Apr 09 '14 at 13:58
  • It's working for me! `$('#comment-21628443').attr('data-x', 5)` then `$('[data-x="5"]')` finds it, then `$('#comment-21628443').data()` prints `Object {x: 5}` – Pedro Moreira Apr 09 '14 at 14:53
  • @PedroMoreira yes but your example is _backwards_ from the scenario I'm pointing out in my answer -- try `$('#comment-21628443').data('x', 5)` instead then `$('[data-x="5"]')` and you get nothing. I'll add some more detail to try to make it clearer. – drzaus Apr 10 '14 at 21:07
  • hm...@PedroMoreira weird, I could have sworn your original comment didn't involve setting the attribute. I was incorrect in saying _"it can only be used for DOM traversal"_ -- you're right, you can access the data later. But be warned: if you later change it via `data` you'll be left with the "stale" value in the attribute even though `$el.data()` returns the right value, which could be confusing. – drzaus Apr 10 '14 at 21:28
  • Yes, I already know that. data() can't be used to set data attributes, they must be set with attr() so they stick in the html. It's ok to retreive them later with data() though. – Pedro Moreira Apr 10 '14 at 21:45
  • 1
    @TomSarduy I still love getting reminders about this question -- you couldn't have provided a better setup for my comment; I really was hoping I'd get to use that quote... – drzaus Dec 11 '15 at 16:17
115

I haven't seen a JavaScript answer without jQuery. Hopefully it helps someone.

var elements = document.querySelectorAll('[data-customerID="22"]');

elements[0].innerHTML = 'it worked!';
<a data-customerID='22'>test</a>

Info:

Sjoerd Pottuit
  • 2,307
  • 4
  • 20
  • 35
73

To select all anchors with the data attribute data-customerID==22, you should include the a to limit the scope of the search to only that element type. Doing data attribute searches in a large loop or at high frequency when there are many elements on the page can cause performance issues.

$('a[data-customerID="22"]');
Travis J
  • 81,153
  • 41
  • 202
  • 273
50

Native JS Examples

Get NodeList of elements

var elem = document.querySelectorAll('[data-id="container"]')

html: <div data-id="container"></div>

Get the first element

var firstElem = document.querySelector('[id="container"]')

html: <div id="container"></div>

Target a collection of nodes which returns a nodelist

document.getElementById('footer').querySelectorAll('[data-id]')

html:

<div class="footer">
    <div data-id="12"></div>
    <div data-id="22"></div>
</div>

Get elements based on multiple (OR) data values

document.querySelectorAll('[data-section="12"],[data-selection="20"]')

html:

<div data-selection="20"></div>
<div data-section="12"></div>

Get elements based on combined (AND) data values

document.querySelectorAll('[data-prop1="12"][data-prop2="20"]')

html:

<div data-prop1="12" data-prop2="20"></div>

Get items where the value starts with

document.querySelectorAll('[href^="https://"]')
etoxin
  • 4,908
  • 3
  • 38
  • 50
  • The selector for "get the first element" is correct but not consistent with the other examples -- I believe it's missing "data-". – GuyPaddock Mar 20 '20 at 23:13
17

The construction like this: $('[data-XXX=111]') isn't working in Safari 8.0.

If you set data attribute this way: $('div').data('XXX', 111), it only works if you set data attribute directly in DOM like this: $('div').attr('data-XXX', 111).

I think it's because jQuery team optimized garbage collector to prevent memory leaks and heavy operations on DOM rebuilding on each change data attribute.

Derk Jan Speelman
  • 11,291
  • 4
  • 29
  • 45
Anton Danilchenko
  • 2,318
  • 1
  • 25
  • 24
  • This helped me a lot - if I used the data or prop methods, then selection by $('...[data-x="y"]') was not working - I used attr instead (it pushes the attribute change to the DOM). Thx – Jarda Jul 27 '17 at 10:25
17

via Jquery filter() method:

http://jsfiddle.net/9n4e1agn/1/

HTML:

<button   data-id='1'>One</button>
<button   data-id='2'>Two</button>

JavaScript:

$(function() {    
    $('button').filter(function(){
        return $(this).data("id")   == 2}).css({background:'red'});  
     });
Razan Paul
  • 13,618
  • 3
  • 69
  • 61
  • Did you try the fiddle? FIlter method is just another approach to achieve the same thing. It can be useful when you already have a set of Jquery objects and you need to filter based on data attribute or anything else. – Razan Paul Jul 20 '15 at 22:53
  • My apologies, @Blizzard. I've commented on the wrong answer. Pasted it to the right one now. #AlwaysALongDayAtWork – Peter B Jul 21 '15 at 14:45
14

For this to work in Chrome the value must not have another pair of quotes.

It only works, for example, like this:

$('a[data-customerID=22]');
user55318
  • 249
  • 2
  • 2
  • 4
    This appears to be incorrect. At least it isn't correct now. I've just used $('[data-action="setStatus"]').removeClass('disabled'); in Chrome and it works perfectly. – Peter B Jul 21 '15 at 14:45
  • I guess there is no use of "" inside the selector, it can be used as `$('[data-action=setStatus]').removeClass('disabled')` – Animesh Singh May 27 '17 at 04:53
9

It's sometimes desirable to filter elements based on whether they have data-items attached to them programmatically (aka not via dom-attributes):

$el.filter(function(i, x) { return $(x).data('foo-bar'); }).doSomething();

The above works but is not very readable. A better approach is to use a pseudo-selector for testing this sort of thing:

$.expr[":"].hasData = $.expr.createPseudo(function (arg) {
    return function (domEl) {
        var $el = $(domEl);
        return $el.is("[" + ((arg.startsWith("data-") ? "" : "data-") + arg) + "]") || typeof ($el.data(arg)) !== "undefined";
    };
});

Now we can refactor the original statement to something more fluent and readable:

$el.filter(":hasData('foo-bar')").doSomething();
XDS
  • 3,786
  • 2
  • 36
  • 56
  • 1
    The first solution misses the return statement, it needs to be: $el.filter(function(i, x) { return $(x).data('foo-bar'); }).doSomething(); – Salma Gomaa Aug 10 '17 at 08:05
5

Just to complete all the answers with some features of the 'living standard' - By now (in the html5-era) it is possible to do it without an 3rd party libs:

  • pure/plain JS with querySelector (uses CSS-selectors):
    • select the first in DOM: document.querySelector('[data-answer="42"],[type="submit"]')
    • select all in DOM: document.querySelectorAll('[data-answer="42"],[type="submit"]')
  • pure/plain CSS
    • some specific tags: [data-answer="42"],[type="submit"]
    • all tags with an specific attribute: [data-answer] or input[type]
Sven
  • 524
  • 4
  • 10
0

It will work :)

$('.ic-star[data-rate="1"]').addClass('rated');

  • 2
    Please don't post code-only answers but add a little textual explanation about how and why your approach works and what makes it different from the other answers given. You can find out more at our ["How to write a good answer"](https://stackoverflow.com/help/how-to-answer) page. – ahuemmer Dec 10 '22 at 08:20