Use the .matches() Method to Determine if a Selector Matches an Element
Tags: javascript, html
Published: 2023-01-22 03:27:36

---
title: Use the .matches() Method to Determine if a Selector Matches an Element
published: true
date: 2023-01-21 05:00:00 UTC
tags: JavaScript, HTML
canonical_url: https://macarthur.me/posts/ways-to-check-if-element-matches-selector
---

I’m rather late to the party, but I recently ran across JavaScript’s `Element.matches()`, which allows you to check if a given CSS selector matches an element. Put another way, it tells you if an element [_would be_](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches)[selected](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches) if the DOM were being queried by something like `document.querySelector()`.

### Why I Needed It

Somewhere in [TypeIt](https://typeitjs.com/), I’m searching an array of nodes for the index of the node that has a specific CSS selector. At a super zoomed-in level during that process, I’m doing a version of this:

```
const nodeIndex = allNodes.findIndex(n => n.matches('.selector'));

```

It’s a little bit of a face-palm moment, because although it’s supported in browsers going back to IE9, I’ve been using less-than-ideal techniques I’ve used in the past. This is post was written to honor them before saying goodbye forever.

## Using Element.closest()

Using `.closest()` allows you to query _up_ through the DOM tree rather than down, and it starts that query with the element on which it’s called. So, if the node itself matches that selector, you’ll get a truthy result. But this approach requires you to verify that the node you’ve found is the same node you’re interested in checking. Otherwise, you’ll still get a truthy result when `.closest()` matches against a parent element that happens to have the same selector.

Wrapping up this functionality might look like so:

```
function hasSelector(element: HTMLElement, selector: string): boolean {
  const matchedNode = node.closest(selector);

  if (!matchedNode) {
    return false;
  }

  return matchedNode === element;
}

```

There’s another downside worth noting. Because `.closest()` will continue traversing up the tree when it doesn’t find a match, you’re taking on some performance baggage. After all, you’re only actually interested in checking _one_ element, but `.closest()` will keep searching all the way up the tree if it hasn’t found a match.

## Checking the Parent’s Children for Same Node

You can’t use `.querySelector()` on an element to check itself for a selector, but you _can_ check each of the parent’s children for it. If any of those matched elements are the same node as the target, you’ve got what you need.

```
function hasSelector(element: HTMLElement, selector: string): boolean {
	if (!element.parentElement) {
		return false;
	}

	return Array.from(element.parentElement.querySelectorAll(selector)).some(
		(node) => node === element
	);
}

```

In terms of performance, this approach is better than the previous because the maximum amount of DOM you’ll crawl is the target element’s direct parent. But readability arguably suffers, and depending on your DOM tree, you still carry some performance overhead because you’re evaluating multiple sibling elements, even though you’re concerned with only one.

## Welcome to the 21st Century

With the `.matches()` method available, neither performance nor verbosity are problems anymore. Only one element — the target element — is ever being handled at a time. On top of that, the API is extremely clear. I’m still kicking myself that this thing has been around for years (and even longer if you consider the [browser-specific implementations](https://sd.blackball.lv/library/DOM_Enlightenment_(2013).pdf) that existed before that). Hope you find it as useful as I have!