0

Suppose I have the following markup...

<div data-namespace-title="foo"></div>
<div data-namespace-description="bar"></div>
<div data-namespace-button="foo"></div>

Is there anyway in which I can select of them with a querySelectorAll?

I've tried document.querySelectorAll([data-namespace-*]), but that doesn't work of course

methodofaction
  • 70,885
  • 21
  • 151
  • 164

3 Answers3

2

There is no easy way to do it, simply because the browser does not implement wildcard selectors on the attribute name/key (only on its value). What you can do is to simply iterate through your element set (in this case, their common denominator is div), and then filter them out.

You can access the list of attributes of each DOM node by calling <Node>.attributes, and then convert that into an array, and check if one or more of each attribute's name matches the regex pattern /^data-namespace-.*/gi:

var els = document.querySelectorAll("div");
var filteredEls = Array.prototype.slice.call(els).filter(function(el) {
  var attributes = Array.prototype.slice.call(el.attributes);
  
  // Determine if attributes matches 'data-namespace-*'
  // We can break the loop once we encounter the first attribute that matches
  for (var i = 0; i < attributes.length; i++) {
    var attribute = attributes[i];
    
    // Return the element if it contains a match, and break the loop
    if (attribute.name.match(/^data-namespace-.*/gi))
        return el;
  }
});

console.log(filteredEls);
<div data-namespace-title="foo">title</div>
<div data-namespace-description="bar">description</div>
<div data-namespace-button="foobar">button</div>
<div data-dummy>dummy</div>

Update: if you're familiar with ES6, it gets a lot cleaner, because:

  • We can use Array.from in place of the cumbersome Array.prototype.slice.call(...). Pro-tip: you can also use the spread operator, i.e. const els = [...document.querySelectorAll("div")].
  • We can use Array.some in place of manually creating a for loop with return logic

const els = Array.from(document.querySelectorAll("div"));
const filteredEls = els.filter(el => {
  const attributes = Array.from(el.attributes);
  
  return attributes.some(attribute => attribute.name.match(/^data-namespace-.*/gi));
});

console.log(filteredEls);
<div data-namespace-title="foo">title</div>
<div data-namespace-description="bar">description</div>
<div data-namespace-button="foobar">button</div>
<div data-dummy>dummy</div>
Terry
  • 63,248
  • 15
  • 96
  • 118
2

Not sure if you would be up for changing the format of you attributes, but making them all the same and adding an extra attribute could be of use if you want to using querySelectorAll

Array.from(document.querySelectorAll('[data-namespace]')).forEach(el => {
  console.log(el.getAttribute('data-value'))
})
<div data-namespace="title" data-value="foo"></div>
<div data-namespace="description" data-value="bar"></div>
<div data-ns="button" data-value="foo"></div>
<div data-namespace="button" data-value="foo"></div>

Your other option is to use xpath.

Note: When using iterateNext() it will break if you modify the document before calling it.

var divs = document.evaluate('//@*[starts-with(name(.), "data-namespace")]', document, null, XPathResult.ANY_TYPE, null);

var div = divs.iterateNext()

while (div) {
  alert(div.ownerElement.textContent)
  div = divs.iterateNext()
}
<div data-namespace-title="foo">Foo</div>
<div data-namespace-description="bar">Bar</div>
<div data-ns-button="foo">NS Foo</div>
<div data-namespace-button="foo">Foo</div>
Get Off My Lawn
  • 34,175
  • 38
  • 176
  • 338
0

There's no built-in selector for such a thing, but you can still accomplish it easily enough, by selecting all elements and then filtering for those which have an attribute that starts with data-namespace:

console.log(
  [...document.querySelectorAll('*')]
    .filter(({ attributes }) =>
      [...attributes].some(({ name }) => name.startsWith('data-namespace'))
    )
);
<div data-baz="baz"></div>
<div data-namespace-title="foo"></div>
<div data-namespace-description="bar"></div>
<div data-namespace-button="foo"></div>
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320