501

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>
6
  • 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). Commented Aug 16, 2011 at 21:06
  • 1
    @hay not at all you can even select these elements in pure css too. Commented Aug 17, 2011 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. Commented Jun 22, 2017 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. Commented Jun 23, 2017 at 7: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" Commented Dec 18, 2018 at 13:44

8 Answers 8

716

You can use querySelectorAll:

document.querySelectorAll('[data-foo]');
Sign up to request clarification or add additional context in comments.

4 Comments

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: code.google.com/p/chromium/issues/detail?id=91637)
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 The original title's intent was inadvertently changed with this edit. I've restored it back now.
document.querySelectorAll("[data-foo='1']") should be document.querySelector("[data-foo='1']")
488
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.

2 Comments

How can you set the values for the elements you get?
@StevenAguilar .querySelectorAll() returns a NodeList. As noted in that documentation, you can iterate over the collection using .forEach(). Note that this is a non-IE solution: developer.mozilla.org/en-US/docs/Web/API/…. If you need to support IE, you'll have to just loop over the NodeList using a regular for loop.
80
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:

Comments

20

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>

3 Comments

Using hasOwnProperty is the best answer for me so far in 2016, this is very fast regarding other ways of iteration Mdn hasOwnProperty
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
How should i do if i want to get the tag value once if found the tag i want buy using querySelectorALL ?
3

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>

Comments

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.

2 Comments

Right, I removed the hint about old browsers.
yeah easy to miss the tag. because it is html5 we are all suggesting document.querySelectorAll (and the data-* attribute is html5 specific too).
-3

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

6 Comments

What is the litany of issues with querySelectorAll?
I would also love to hear about these issues.
Now, we'll never know which litany was that. One more chapter for the Eternal Mysteries from SO
downvoting this. It is completely over coded and unnecessary with the querySelectorAll api
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,
|
-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/

6 Comments

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.
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 --> jsbin.com/ipisul/edit#javascript,html
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.
@shawndumas - it appears whatever you were having was a PEBKAC. 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 - does the jsbin.com/ipisul one work for you? cause your jsfiddle one is not working in my (work-place demanded) ie9...
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.