276

Is there a way to do a wildcard element name match using querySelector or querySelectorAll?

The XML document I'm trying to parse is basically a flat list of properties

  • I need to find elements that have certain strings in their names.
  • I see support for wildcards in attribute queries but not for the elements themselves.

Any solution except going back to using the apparently deprecated XPath (IE9 dropped it) is acceptable.

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
Erik Andersson
  • 2,761
  • 2
  • 14
  • 3

8 Answers8

648

[id^='someId'] will match all ids starting with someId.

[id$='someId'] will match all ids ending with someId.

[id*='someId'] will match all ids containing someId.

If you're looking for the name attribute just substitute id with name.

If you're talking about the tag name of the element I don't believe there is a way using querySelector

Robin van Baalen
  • 3,632
  • 2
  • 21
  • 35
JaredMcAteer
  • 21,688
  • 5
  • 49
  • 65
  • 4
    Thanks, I meant the tag name. – Erik Andersson Jan 03 '12 at 15:32
  • hmm I can't do document.querySelectorAll("div.[id$='foo']") – SuperUberDuper Feb 11 '16 at 15:56
  • 6
    The period in there is probably messing it up `document.querySelectorAll("div[id$='foo']")` IE8 only has partial QSA support, I think they only support CSS2.1 selectors, so these selectors won't work in IE8, but IE9+ will work. – JaredMcAteer Feb 11 '16 at 20:22
  • I wonder why the API is not closer to the regex way, i.e [id='^someId'] will match all ids starting with someId. I am sure there is a reason, but I can't figure out why. One issue is of course how to distinguish when the id is actually ^someId. As a rough idea, maybe it would be something like [id=^'someId'] , i.e the ^ character is outside of the string ? – sktguha Jan 17 '21 at 08:36
  • Attribute Selectors initially supported `=`, `|=` and `~=` in CSS2 which was published over 20 years ago in 1998. It wasn't until 3 or 4 years after that working drafts of CSS3 included the new substring selectors, and many years after that for mainstream browser support, so it's most likely they were done this way to maintain consistency with the original selectors. As for why the original selectors didn't use Regexp you'd have to dig through archived mailing lists and meeting notes to figure that one out. – JaredMcAteer Jan 18 '21 at 17:31
  • Thanks so much for info! Is there a documentation where I can find more info on related rules? – 10101010 Apr 01 '21 at 09:03
  • 1
    Heres the main website for the CSS specification https://www.w3.org/Style/CSS/specs.en.html and the most recent information on attribute selectors https://www.w3.org/TR/selectors-4/#attribute-selectors – JaredMcAteer Apr 03 '21 at 15:52
  • @10101010 also friendlier version is [here](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors). – shriek May 08 '21 at 21:14
50

I was messing/musing on one-liners involving querySelector() & ended up here, & have a possible answer to the OP question using tag names & querySelector(), with credits to @JaredMcAteer for answering MY question, aka have RegEx-like matches with querySelector() in vanilla Javascript

Hoping the following will be useful & fit the OP's needs or everyone else's:

// basically, of before:
var youtubeDiv = document.querySelector('iframe[src="http://www.youtube.com/embed/Jk5lTqQzoKA"]')

// after     
var youtubeDiv = document.querySelector('iframe[src^="http://www.youtube.com"]');
// or even, for my needs
var youtubeDiv = document.querySelector('iframe[src*="youtube"]');

Then, we can, for example, get the src stuff, etc ...

console.log(youtubeDiv.src);
//> "http://www.youtube.com/embed/Jk5lTqQzoKA"
console.debug(youtubeDiv);
//> (...)
TylerH
  • 20,799
  • 66
  • 75
  • 101
StephaneAG
  • 1,107
  • 1
  • 12
  • 11
17

I liked many of the answers above, but I prefer my queries run only on classes/IDs so they don't have to iterate over every element. This is a combination of code from both @bigiCrab and @JaredMcAteer

// class exactly matches abc
const exactAbc = document.querySelectorAll("[class='abc']")

// class begins with abc
const startsAbc = document.querySelectorAll("[class^='abc']")

// class contains abc
const containsAbc = document.querySelectorAll("[class*='abc']")

// class contains white-space separated word exactly matching abc
const wordAbc = document.querySelectorAll("[class~='abc']")

// class ends with abc
const endsAbc = document.querySelectorAll("[class$='abc']")

Substitute "class" with "id" or "href" to get other matches. Read the article linked below for further examples.

Reference:

  1. CSS attribute selectors on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors
Dani Amsalem
  • 1,266
  • 17
  • 26
16

Set the tagName as an explicit attribute:

for(var i=0,els=document.querySelectorAll('*'); i<els.length;
          els[i].setAttribute('tagName',els[i++].tagName) );

I needed this myself, for an XML Document, with Nested Tags ending in _Sequence. See JaredMcAteer answer for more details.

document.querySelectorAll('[tagName$="_Sequence"]')

I didn't say it would be pretty :) PS: I would recommend to use tag_name over tagName, so you do not run into interferences when reading 'computer generated', implicit DOM attributes.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Lorenz Lo Sauer
  • 23,698
  • 16
  • 85
  • 87
12

I just wrote this short script; seems to work.

/**
 * Find all the elements with a tagName that matches.
 * @param {RegExp} regEx  regular expression to match against tagName
 * @returns {Array}       elements in the DOM that match
 */
function getAllTagMatches(regEx) {
  return Array.prototype.slice.call(document.querySelectorAll('*')).filter(function (el) { 
    return el.tagName.match(regEx);
  });
}
getAllTagMatches(/^di/i); // Returns an array of all elements that begin with "di", eg "div"
Utopiah
  • 306
  • 1
  • 11
cruzanmo
  • 747
  • 8
  • 8
  • object returned by querySelectorAll does not necessarily support filter on all browsers(as it is not always a regular javascript array). Make sure to check this assertion in production, either before the script is generated(if the script is generated dynamically), or using condition statements. – Dmytro Oct 17 '18 at 16:38
  • Very nice answer... This was the closes match I found to my issue. I'm working with custom elements and features like this one are still manual work, sadly. – codepleb Jan 30 '20 at 15:46
8

i'm looking for regex + not + multiClass selector, and this is what I got.

Hope this help someone looking for same thing!

// contain abc class
"div[class*='abc']" 

// contain exact abc class
"div[class~='abc']" 

// contain exact abc & def(case-insensitively)
"div[class~='abc'][class*='DeF'i]" 

// contain exact abc but not def(case-insensitively)
"div[class~='abc']:not([class*='DeF'i])" 

css selector doc: https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors

simple test: https://codepen.io/BIgiCrab/pen/BadjbZe

bigiCrab
  • 91
  • 1
  • 4
1

For example, there is a form as shown below:

<form id="my-form" method="post">
  <input type="text" name="my-first-name" value="John">
  <input type="text" name="my last name" value="Smith">
</form>

Then, you can use these selectors below according to 6.1. Attribute presence and value selectors and 6.2. Substring matching attribute selectors.

input[name] selects all <input>s which have name attribute:

document.querySelectorAll('input[name]');
// "my-first-name" and "my last name"

input[name="my last name"] selects all <input>s whose name attribute is exactly my last name:

document.querySelectorAll('input[name="my last name"]');
// "my last name"

input[name~="name"] selects all <input>s whose name attribute's whitespace-separated words contain my last name:

document.querySelectorAll('input[name~="name"]');
// "my last name"

input[name|="my"] selects all <input>s whose name attribute starts from my with - so my-:

document.querySelectorAll('input[name|="my"]');
// "my-first-name"

input[name|="my last name"] selects all <input>s whose name attribute is exactly my last name:

document.querySelectorAll('input[name|="my last name"]');
// "my last name"

input[name^="my "] selects all <input>s whose name attribute starts from my :

document.querySelectorAll('input[name^="my "]');
// "my last name"

input[name$="ame"] selects all <input>s whose name attribute ends with ame:

document.querySelectorAll('input[name$="ame"]');
// "my-first-name" and "my last name"

input[name*="st-"] selects all <input>s whose name attribute contains st-:

document.querySelectorAll('input[name*="st-"]');
// "my-first-name"

In addition, input:not([name="my last name"]) selects all <input>s whose name attribute doesn't contain my last name according to 4.3. The Negation (Matches-None) Pseudo-class: ':not()':

document.querySelectorAll('input:not([name="my last name"])');
// "my-first-name"

And, you can put form#my-form in front of input[name^="my"] to select <input>s more specifically like logical AND (&&) as shown below:

document.querySelectorAll('form#my-form input[name^="my"]');
// "my-first-name" and "my last name"

And, you can also do input:not([name^="last"]):not([name*="st"][value="Smith"]) to select <input>s more specifically like logical AND (&&) as shown below:

document.querySelectorAll('input:not([name^="my-"]):not([name*="st"][value="John"])');
// "my last name"

And, you can also do input[name*="first"], input[name="my last name"] to select <input>s more specifically like logical OR (||) as shown below:

document.querySelectorAll('input[name*="first"], input[name="my last name"]')
// "my-first-name" and "my last name"
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
-2

There is a way by saying what is is not. Just make the not something it never will be. A good css selector reference: https://www.w3schools.com/cssref/css_selectors.asp which shows the :not selector as follows:

:not(selector)  :not(p) Selects every element that is not a <p> element

Here is an example: a div followed by something (anything but a z tag)

div > :not(z){
 border:1px solid pink;
}
Steve Lloyd
  • 773
  • 2
  • 12
  • 33