loading...

Matching elements with selectors in JS

samthor profile image Sam Thorogood Updated on ・2 min read

Blog-A-Day in June (19 Part Series)

1) Rebuild only when necessary in Node 2) Civilization is a game you never lose 3 ... 17 3) Arrow functions break JavaScript parsers 4) Detecting Select All on the Web 5) Declaring JS Variables in 2019 6) Sam's dotfiles highlights 7) Automate Reading Form Results with 🤖 Chrome 8) Beyond appendChild: Better convenience methods for HTML 9) AMA, Sam 10-yr Googler in Web DevRel 10) Disable a HTML form while in-flight using fieldset 11) PWAs that download like apps 🗜️ 12) Matching elements with selectors in JS 13) Install This PWA To Continue 14) Google Assistant now supports "Open/Close" devices 15) Modern Web Components 16) What To Expect When You're Expecting To Drop IE11 🗑️ 17) Divert Vertical Scroll To The Side ↔️ 18) Graceful Shutdown Is A Lie 19) Progress Indicator With Fetch

Another short one! Let's talk about the two HTML helper methods, Element.matches and Element.closest. Both of these methods are supported in modern, evergreen browsers. 💚

These are both just nice helper methods that make your life writing HTML/JS easier! 🌈

Matches

The first, .matches, is a way to check if an element matches a selector. Selectors are .foo, #bar, or form[method="POST"], just like you'd use in querySelector or a CSS rule.

This is just a simpler way to check a condition. For instance, we can replace all these methods with a more generic equivalent:

el.classList.contains('foo') /* == */ el.matches('.foo');
el.hasAttribute('hello')     /* == */ el.matches('[hello]');
el.id === 'bar'              /* == */ el.matches('#bar');

In addition, we can write more complex queries in a single request. How would I match a button[type="foo"].green? I can simply pass that whole string to el.matches! 🤯

Closest

The second method, .closest, is a practical extension of .matches. Instead of checking just the current element, it checks itself and all parent elements- until it finds one that does match. 🆙

This is very useful when you're adding an event handler to a group of buttons or elements. Here's an example:

Where we add ze handler 〰️

Rather than adding a handler to every button- maybe because you'll be adding more buttons as we go along, but also because you're paying more memory and performance cost adding every single handler- we can just add a single handler to the parent. 🤔

To find out what button we actually clicked on- because perhaps the click was on an inner element (such as the <strong> above), our event handler might look like this:

root.addEventListener('click', (event) => {
  const button = event.target.closest('button');
  if (!button) {
    return;  // clicked on the root itself or something else ¯\_(ツ)_/¯
  }
  // ... do something with the button we found!
});

For me, I'll often set attributes like data-name or data-type so I can do something programatically with each button or thing that's under your root. 🌲👍

Thanks!

That's all for today! Hope you've learned something.

13 👋

Blog-A-Day in June (19 Part Series)

1) Rebuild only when necessary in Node 2) Civilization is a game you never lose 3 ... 17 3) Arrow functions break JavaScript parsers 4) Detecting Select All on the Web 5) Declaring JS Variables in 2019 6) Sam's dotfiles highlights 7) Automate Reading Form Results with 🤖 Chrome 8) Beyond appendChild: Better convenience methods for HTML 9) AMA, Sam 10-yr Googler in Web DevRel 10) Disable a HTML form while in-flight using fieldset 11) PWAs that download like apps 🗜️ 12) Matching elements with selectors in JS 13) Install This PWA To Continue 14) Google Assistant now supports "Open/Close" devices 15) Modern Web Components 16) What To Expect When You're Expecting To Drop IE11 🗑️ 17) Divert Vertical Scroll To The Side ↔️ 18) Graceful Shutdown Is A Lie 19) Progress Indicator With Fetch

Posted on Jun 13 '19 by:

samthor profile

Sam Thorogood

@samthor

Developer Relations for Web at Google.

Discussion

markdown guide
 

Just a nitpick:
The following line:

el.classList.has('foo') /* becomes */ el.matches('.foo');

I think you mean 'contains' instead of 'has'

  • Uncaught TypeError: elem.classList.has is not a function
 

Oh yeah, I think I was thinking of Set or Map. I'll fix, thanks! 👍

 

Good post!

And if you need to support IE11, the polyfill is very light. You can find examples on the MDN pages:

Or you can just use Babel.

 

Babel on its own is not a magic bullet—you need to include the right polyfill with it, since it's not a language feature. But your point is valid and the polyfills are easy.

IE11 is ~2%, for me on any modern content site, I just don't ship it any JS whatsoever. This is what we did for e.g. the Chrome Dev Summit site.

 

Yeah you need to include the Babel polyfill properly.

Haha, I'm jealous. Unfortunately the website I'm working on is B2B focused which has a significant number of enterprise users in IE11. Ah well.

 

I used closest to refactor some recursive code recently! It's a super cool method. I didn't know about matches though -- so thanks 😊

How are you finding daily code blogging?

 

Matches is probably less useful but is really a precursor to closest.

Daily blogging is going well but—having a newborn is complex, we've lots of medical appointments—so I've slipped a bit so I'm now making each post about ~2 hours before my artificially self-imposed deadline (midnight on the day, in Sydney time).

I have a remarkably large short list of topics that I'd like to blog about, so I'm not low on ideas. I just need to build up a buffer again 🤣

 

Nice tip Sam! Keep up the good work!