ChatGPT解决这个技术问题 Extra ChatGPT

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

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>
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).
@hay not at all you can even select these elements in pure css too.
@JamesHay because not every environment, company, site, coding standard, what have you, allows for the use of jQuery. jQuery is not irreplaceable.
@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.
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"

J
Joe

You can use querySelectorAll:

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

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.
J
Joseph Marikle
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.


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.
M
Metro Smurf
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:

MDN: data-*

MDN: Using data attributes


U
Uwe Keim

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>

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
H
Heinrich Ulbricht

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.


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).
B
Brian
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/


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...
佚名

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


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,