130
<p data-foo="bar">

How can you do the equivalent to

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

where querySelectorAll is not available?

I need a native solution that works at least in IE7. I don’t care about IE6.

Anders
  • 8,307
  • 9
  • 56
  • 88
ryanve
  • 50,076
  • 30
  • 102
  • 137
  • The working solution that I used is in https://github.com/ryanve/dope/blob/master/dope.js in the method called 'queryAttr' – ryanve Nov 24 '12 at 19:39
  • 7
    Lol, your question is my answer. So this come with another question. In what situation that `querySelectorAll` is not available? `note - I don't care all IE` – vzhen Jul 31 '13 at 14:14
  • @vzhen [See QSA availability here](http://caniuse.com/#feat=queryselector). Note QSA is limited to the CSS selectors supported by the browser. Compare [CSS2 support](http://caniuse.com/#feat=css-sel2) to [CSS3 support](http://caniuse.com/#search=css-sel3). IE8 supports CSS2 but not CSS3 selectors. – ryanve Jul 31 '13 at 17:22
  • check out the [sizzle.js](http://sizzlejs.com/) javascript selector library – epoch Feb 29 '12 at 09:28
  • 1
    Nice yea the only selecting I need to do is data attributes so I was trying to figure the simplest way to patch that without pulling in a whole selector engine like Sizzle. But good point to look in the source. BTW another great selector engine is https://github.com/ded/qwery – ryanve Feb 29 '12 at 09:45

8 Answers8

142

You could write a function that runs getElementsByTagName('*'), and returns only those elements with a "data-foo" attribute:

function getAllElementsWithAttribute(attribute)
{
  var matchingElements = [];
  var allElements = document.getElementsByTagName('*');
  for (var i = 0, n = allElements.length; i < n; i++)
  {
    if (allElements[i].getAttribute(attribute) !== null)
    {
      // Element exists with attribute. Add to array.
      matchingElements.push(allElements[i]);
    }
  }
  return matchingElements;
}

Then,

getAllElementsWithAttribute('data-foo');
Vinay Aggarwal
  • 1,565
  • 1
  • 10
  • 19
kevinfahy
  • 1,544
  • 1
  • 11
  • 7
  • 8
    Using `!= null` is the ideal way (better than my comment above) because in old IE it is possible for getAttribute to return a value whose `typeof` is `'number'` – ryanve Nov 24 '12 at 19:51
  • 1
    Why using `document.getElementsByTagName('*')` instead of `document.all`? – pedrozath Jun 11 '16 at 18:09
  • 2
    Why not use `hasAttribute` rather than `getAttribute() !== null`, since you only want to check for existence and not its value? – rvighne Jul 12 '16 at 23:00
66

Use

//find first element with "someAttr" attribute
document.querySelector('[someAttr]')

or

//find all elements with "someAttr" attribute
document.querySelectorAll('[someAttr]') 

to find elements by attribute. It's now supported in all relevant browsers (even IE8): http://caniuse.com/#search=queryselector

Pylinux
  • 11,278
  • 4
  • 60
  • 67
  • 2
    How does this have so many upvotes when the question explicitly requests: "I need a **native solution** that works at **least in IE7**". Secondly, that link states that support starts in IE11 even though it actually starts from IE8 - maybe this should be swapped to https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll so it actually supports the answer...? – Zze Sep 12 '17 at 20:26
  • 8
    The reason for all the upvotes, and the reason I made the answer, is that this SO question is _really_ old, so when you search for how to find DOM elements you find this question very high in the search results, and since it's what people are looking for they upvote. Usefulness > historical accuracy. Secondly the link still works just fine, it's just that caniuse.com has hidden old browsers, if you switch to "Usage relative" you'll still see the old browsers. – Pylinux Sep 13 '17 at 13:38
  • Worked perfectly. Quick and simple – Dawson B Nov 18 '17 at 23:35
45

I played a bit around and ended up with this crude solution:

function getElementsByAttribute(attribute, context) {
  var nodeList = (context || document).getElementsByTagName('*');
  var nodeArray = [];
  var iterator = 0;
  var node = null;

  while (node = nodeList[iterator++]) {
    if (node.hasAttribute(attribute)) nodeArray.push(node);
  }

  return nodeArray;
}

The usage is quite simple, and works even in IE8:

getElementsByAttribute('data-foo');
// or with parentNode
getElementsByAttribute('data-foo', document);

http://fiddle.jshell.net/9xaxf6jr/

But I recommend to use querySelector / All for this (and to support older browsers use a polyfill):

document.querySelectorAll('[data-foo]');
yckart
  • 32,460
  • 9
  • 122
  • 129
  • Aye, +1 for querySelectorAll. A quick jsperf test http://jsperf.com/custom-vs-selectorall-attributes shows that it's much faster than the accepted answer... unfortunately it's not IE 7 compatible :( – Sebastien Daniel Apr 15 '15 at 19:19
18

Try this it works

document.querySelector('[attribute="value"]')

example :

document.querySelector('[role="button"]')
BrainabilGH
  • 464
  • 5
  • 15
5

That works too:

document.querySelector([attribute="value"]);

So:

document.querySelector([data-foo="bar"]);
Icarus
  • 1,627
  • 7
  • 18
  • 32
CallMarl
  • 524
  • 4
  • 15
  • 2
    This is missing the single quotes in the actual querySelector. Should be: `document.querySelector('[data-foo="bar"]');` – Brettins Jul 24 '17 at 01:36
1

A little modification on @kevinfahy 's answer, to allow getting the attribute by value if needed:

function getElementsByAttributeValue(attribute, value){
  var matchingElements = [];
  var allElements = document.getElementsByTagName('*');
  for (var i = 0, n = allElements.length; i < n; i++) {
    if (allElements[i].getAttribute(attribute) !== null) {
      if (!value || allElements[i].getAttribute(attribute) == value)
        matchingElements.push(allElements[i]);
    }
  }
  return matchingElements;
}
Bernardo Dal Corno
  • 1,858
  • 1
  • 22
  • 27
0

Try this - I slightly changed the above answers:

var getAttributes = function(attribute) {
    var allElements = document.getElementsByTagName('*'),
        allElementsLen = allElements.length,
        curElement,
        i,
        results = [];

    for(i = 0; i < allElementsLen; i += 1) {
        curElement = allElements[i];

        if(curElement.getAttribute(attribute)) {
            results.push(curElement);
        }
    }

    return results;
};

Then,

getAttributes('data-foo');
Surender Lohia
  • 349
  • 3
  • 12
-1

Don't use in Browser

In the browser, use document.querySelect('[attribute-name]').

But if you're unit testing and your mocked dom has a flakey querySelector implementation, this will do the trick.

This is @kevinfahy's answer, just trimmed down to be a bit with ES6 fat arrow functions and by converting the HtmlCollection into an array at the cost of readability perhaps.

So it'll only work with an ES6 transpiler. Also, I'm not sure how performant it'll be with a lot of elements.

function getElementsWithAttribute(attribute) {
  return [].slice.call(document.getElementsByTagName('*'))
    .filter(elem => elem.getAttribute(attribute) !== null);
}

And here's a variant that will get an attribute with a specific value

function getElementsWithAttributeValue(attribute, value) {
  return [].slice.call(document.getElementsByTagName('*'))
    .filter(elem => elem.getAttribute(attribute) === value);
}
gargantuan
  • 8,888
  • 16
  • 67
  • 108