433

Using only pure JavaScript, what is the most efficient way to select all DOM elements that have a certain data- attribute (let's say data-foo).

The elements may be different, for example:

<p data-foo="0"></p><br/><h6 data-foo="1"></h6>
robinCTS
  • 5,746
  • 14
  • 30
  • 37
JLeonard
  • 8,520
  • 7
  • 36
  • 36
  • Keep in mind that `document.querySelectorAll` does not work on IE7. You would have to create a fallback script which would *walk* the DOM tree and checking for attribute in each tag (actually i have no idea how fast `querySelectorAll` is, and would go for manual check of tags). – tereško Aug 16 '11 at 21:06
  • What's your reason for not using jQuery? It's pretty much irreplacable in situations like this... – James Hay Aug 16 '11 at 21:17
  • 1
    @hay not at all you can even select these elements in pure css too. – Knu Aug 17 '11 at 10:05
  • 3
    @JamesHay because not every environment, company, site, coding standard, what have you, allows for the use of jQuery. jQuery is not irreplaceable. – Carnix Jun 22 '17 at 16:25
  • 1
    @Carnix Agreed. It would no longer make sense to use jQuery unless you were already using it, even then I'd probably opt-out. 6 years ago it was a lot more common to have jQuery in your site, supporting IE5-8 was more common, and jQuery provided the abstractions do this in a simple one liner. – James Hay Jun 23 '17 at 07:01
  • 5
    I still dont see any answer that really works on **different** `data-` elements, ie: `data-foo=0` and `data-bar=1` **and** `data-app="js"` **and** `data-date="20181231"` – Alex Dec 18 '18 at 13:44

8 Answers8

648

You can use querySelectorAll:

document.querySelectorAll('[data-foo]');
Joe
  • 80,724
  • 18
  • 127
  • 145
  • 13
    Perfect, thanks! Semi-related note: if you want to select an attribute with a colon in the name, you need to escape the colon (at least in Chrome) like so: querySelectorAll('[attribute\\:name]') (see: http://code.google.com/p/chromium/issues/detail?id=91637) – Jeremy Oct 03 '12 at 18:29
  • 4
    this doesn't actually answer the title of the question. I don't want to select "data-foo". I want to select "data-*" as in "data-foobar", "data-bar", "data-name-i-dont-know" – gman Jul 28 '20 at 03:10
  • 2
    @gman The original title's intent was inadvertently changed with [this edit](https://stackoverflow.com/review/suggested-edits/11216335). I've restored it back now. – robinCTS Dec 27 '20 at 02:12
426
document.querySelectorAll("[data-foo]")

will get you all elements with that attribute.

document.querySelectorAll("[data-foo='1']")

will only get you ones with a value of 1.

Joseph Marikle
  • 76,418
  • 17
  • 112
  • 129
  • How can you set the values for the elements you get? – Steven Aguilar Dec 20 '19 at 21:30
  • 2
    @StevenAguilar `.querySelectorAll()` returns a [`NodeList`](https://developer.mozilla.org/en-US/docs/Web/API/NodeList). As noted in that documentation, you can iterate over the collection using [`.forEach()`](https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach). Note that this is a non-IE solution: https://developer.mozilla.org/en-US/docs/Web/API/NodeList#bcd:api.NodeList. If you need to support IE, you'll have to just loop over the NodeList using a regular `for` loop. – Joseph Marikle Dec 23 '19 at 15:38
64
document.querySelectorAll('[data-foo]')

to get list of all elements having attribute data-foo

If you want to get element with data attribute which is having some specific value e.g

<div data-foo="1"></div>
<div data-foo="2"></div>

and I want to get div with data-foo set to "2"

document.querySelector('[data-foo="2"]')

But here comes the twist ... what if I want to match the data attirubte value with some variable's value? For example, if I want to get the elements where data-foo attribute is set to i

var i=2;

so you can dynamically select the element having specific data element using template literals

document.querySelector(`[data-foo="${i}"]`)

Note even if you don't write value in string it gets converted to string like if I write

<div data-foo=1></div>

and then inspect the element in Chrome developer tool the element will be shown as below

<div data-foo="1"></div>

You can also cross verify by writing below code in console

console.log(typeof document.querySelector(`[data-foo="${i}"]`).dataset('dataFoo'))

why I have written 'dataFoo' though the attribute is data-foo reason dataset properties are converted to camelCase properties

I have referred below links:

Xan
  • 74,770
  • 16
  • 179
  • 206
Ankit Tiwari
  • 1,069
  • 10
  • 15
19

Try it → here

    <!DOCTYPE html>
    <html>
        <head></head>
        <body>
            <p data-foo="0"></p>
            <h6 data-foo="1"></h6>
            <script>
                var a = document.querySelectorAll('[data-foo]');

                for (var i in a) if (a.hasOwnProperty(i)) {
                    alert(a[i].getAttribute('data-foo'));
                }
            </script>
        </body>
    </html>
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
shawndumas
  • 1,413
  • 15
  • 17
  • 1
    Using hasOwnProperty is the best answer for me so far in 2016, this is very fast regarding other ways of iteration [Mdn hasOwnProperty](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) – NVRM Jun 29 '16 at 03:08
  • 1
    NodeList from querySelectorAll() is iterable (though not an array). Looping with `for in` will iterate over the length and item properties. Instead, use `for of` to iterate over properties designed to be iterated over – png Mar 21 '19 at 04:30
  • How should i do if i want to get the tag value once if found the tag i want buy using querySelectorALL ? – Kinesis Feb 03 '23 at 07:38
2

Here is an interesting solution: it uses the browsers CSS engine to to add a dummy property to elements matching the selector and then evaluates the computed style to find matched elements:

It does dynamically create a style rule [...] It then scans the whole document (using the much decried and IE-specific but very fast document.all) and gets the computed style for each of the elements. We then look for the foo property on the resulting object and check whether it evaluates as “bar”. For each element that matches, we add to an array.

Heinrich Ulbricht
  • 10,064
  • 4
  • 54
  • 85
2

Native JavaScript's querySelector and querySelectorAll methods can be used to target the element(s). Use a template string if your dataset value is a variable.

var str = "term";
var term = document.querySelectorAll(`[data-type=${str}]`);
console.log(term[0].textContent);

var details = document.querySelector('[data-type="details"]');
console.log(details.textContent);
<dl>
  <dt data-type="term">Thing</dt>
  <dd data-type="details">The most generic type.</dd>
</dl>
Ronnie Royston
  • 16,778
  • 6
  • 77
  • 91
-4
var matches = new Array();

var allDom = document.getElementsByTagName("*");
for(var i =0; i < allDom.length; i++){
    var d = allDom[i];
    if(d["data-foo"] !== undefined) {
         matches.push(d);
    }
}

Not sure who dinged me with a -1, but here's the proof.

http://jsfiddle.net/D798K/2/

Brian
  • 2,772
  • 15
  • 12
  • 4
    your mostly "right" just not correct. Im pretty sure someone gave you the -1 because your doing alot of extra work to get the elements, and then putting the collection in an array. I didnt give the -1 just dislike when theres no explanation to one. – Loktar Aug 16 '11 at 20:51
  • 2
    expensive (all elements on the page), also use the array literal notation (i.e. []), and on top of it, it does not work. see for yourself --> http://jsbin.com/ipisul/edit#javascript,html – shawndumas Aug 16 '11 at 20:55
  • 2
    Though the OP is using HTML 5 anyways, `getElementsByTagName` with a global (`*`) selector is broken in older IE builds. This is where a recursive DOM search gets the job done. There is also no "data-foo" property on an ElementNode that's mapped from the `data-foo` attribute. You're looking for the `dataset` object (ie: `node.dataset.foo`. –  Aug 16 '11 at 20:57
  • @shawndumas - it appears whatever you were having was a PEBKAC. http://jsfiddle.net/D798K/2/. It works. Ultimately, I'd -1 myself for this answer anyways - I missed the words "most efficient" in the OP's question... – Brian Aug 17 '11 at 09:54
  • @Brian - does the http://www.jsbin.com/ipisul one work for you? cause your jsfiddle one is not working in my (work-place demanded) ie9... – shawndumas Aug 17 '11 at 11:25
  • @shawndumas - Indeed it does - throws me three blank alerts ... no id for the matching nodes... IE8... – Brian Aug 17 '11 at 14:37
-4

While not as pretty as querySelectorAll (which has a litany of issues), here's a very flexible function that recurses the DOM and should work in most browsers (old and new). As long as the browser supports your condition (ie: data attributes), you should be able to retrieve the element.

To the curious: Don't bother testing this vs. QSA on jsPerf. Browsers like Opera 11 will cache the query and skew the results.

Code:

function recurseDOM(start, whitelist)
{
    /*
    *    @start:        Node    -    Specifies point of entry for recursion
    *    @whitelist:    Object  -    Specifies permitted nodeTypes to collect
    */

    var i = 0, 
    startIsNode = !!start && !!start.nodeType, 
    startHasChildNodes = !!start.childNodes && !!start.childNodes.length,
    nodes, node, nodeHasChildNodes;
    if(startIsNode && startHasChildNodes)
    {       
        nodes = start.childNodes;
        for(i;i<nodes.length;i++)
        {
            node = nodes[i];
            nodeHasChildNodes = !!node.childNodes && !!node.childNodes.length;
            if(!whitelist || whitelist[node.nodeType])
            {
                //condition here
                if(!!node.dataset && !!node.dataset.foo)
                {
                    //handle results here
                }
                if(nodeHasChildNodes)
                {
                    recurseDOM(node, whitelist);
                }
            }
            node = null;
            nodeHasChildNodes = null;
        }
    }
}

You can then initiate it with the following:

recurseDOM(document.body, {"1": 1}); for speed, or just recurseDOM(document.body);

Example with your specification: http://jsbin.com/unajot/1/edit

Example with differing specification: http://jsbin.com/unajot/2/edit

  • 25
    What is the litany of issues with `querySelectorAll`? – ShreevatsaR Apr 05 '16 at 01:53
  • 9
    I would also love to hear about these issues. – Sean_A91 May 13 '16 at 01:25
  • 5
    Now, we'll never know which litany was that. One more chapter for the Eternal Mysteries from SO – brasofilo Jan 30 '19 at 13:37
  • downvoting this. It is completely over coded and unnecessary with the `querySelectorAll` api – dman Oct 29 '19 at 17:24
  • 2
    A little over the top for the question maybe, but it's a well coded solution, in pure vanilla, as the OP asked. If you didn't already know how slow document.querySelectorAll is compared to the numerous other ways you can iterate over and find elements, you shouldn't have downvoted this. I think it's a cool demonstration of what is possible, – Kim Skogsmo Jun 18 '21 at 08:49