Select all elements with a "data-xxx" attribute without using jQuery

JavascriptHtmlDom

Javascript Problem Overview


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>

Javascript Solutions


Solution 1 - Javascript

You can use querySelectorAll:

document.querySelectorAll('[data-foo]');

Solution 2 - Javascript

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.

Solution 3 - Javascript

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 like I want to get element if 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:

Solution 4 - Javascript

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>

Solution 5 - Javascript

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.

Solution 6 - Javascript

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/

Solution 7 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJLeonardView Question on Stackoverflow
Solution 1 - JavascriptJoeView Answer on Stackoverflow
Solution 2 - JavascriptJoseph MarikleView Answer on Stackoverflow
Solution 3 - JavascriptAnkit TiwariView Answer on Stackoverflow
Solution 4 - JavascriptshawndumasView Answer on Stackoverflow
Solution 5 - JavascriptHeinrich UlbrichtView Answer on Stackoverflow
Solution 6 - JavascriptBrianView Answer on Stackoverflow
Solution 7 - Javascriptuser1385191View Answer on Stackoverflow