event.target.closest
Tags: dom, closest, methods, domscripting
Published: 2023-11-28 11:24:34

---
title: event.target.closest
published: true
date: 2023-10-12 09:09:30 UTC
tags: dom,closest,methods,domscripting
canonical_url: https://adactio.com/journal/20551
---

[Eric mentioned the JavaScript `closest` method](https://mastodon.social/@Meyerweb/111219441257570897). I use it all the time.

When I wrote the book [DOM Scripting](https://domscripting.com/) back in 2005, I’d estimate that 90% of the JavaScript I was writing boiled down to:

1. Find these particular elements in the DOM and
2. When the user clicks on one of them, do something.

It wasn’t just me either. I reckon that was 90% of most JavaScript on the web: progressive disclosure widgets, accordions, carousels, and so on.

That’s one of the reasons why jQuery became so popular. That first step (“find these particular elements in the DOM”) used to be a finicky affair involving [`getElementsByTagName`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName), [`getElementById`](https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment/getElementById), and other long-winded DOM methods. jQuery came along and allowed us to use CSS selectors.

These days, we don’t need jQuery for that because we’ve got [`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) and [`querySelectorAll`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) (and we can [thank jQuery](https://adactio.com/journal/10806) for their existence).

Let’s say you want to add some behaviour to every `button` element with a `class` of `special`. Or maybe you use a `data-` attribute instead of the `class` attribute; the same principle applies. You want something special to happen when the user clicks on one of those buttons.

1. Use `querySelectorAll('button.special')` to get a list of all the right elements,
2. Loop through the list, and
3. Attach `addEventListener('click')` to each element.

That’s fine for a while. But if you’ve got a lot of special buttons, you’ve now got a lot of event listeners. You might be asking the browser to do a lot of work.

There’s another complication. The code you’ve written runs once, when the page loads. Suppose the contents of the page have changed in the meantime. Maybe elements are swapped in and out using [Ajax](https://bulletproofajax.com/). If a new special button shows up on the page, it won’t have an event handler attached to it.

You can switch things around. Instead of adding lots of event handlers to lots of elements, you can add one event handler to the root element. Then figure out whether the element that just got clicked is special or not.

That’s where [`closest`](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest) comes in. It makes this kind of event handling pretty straightforward.

To start with, attach the event listener to the document:

```
document.addEventListener('click', doSomethingSpecial, false);

```

That function `doSomethingSpecial` will be executed whenever the user clicks on anything. Meanwhile, if the contents of the document are updated via Ajax, no problem!

Use the [`closest`](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest) method in combination with the [`target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target) property of the event to figure out whether that click was something you’re interested in:

```
function doSomethingSpecial(event) {
  if (event.target.closest('button.special')) {
    // do something
  }
}

```

There you go. Like `querySelectorAll`, the `closest` method takes a CSS selector—[thanks again, jQuery](https://css-tricks.com/now-ever-might-not-need-jquery/)!

Oh, and if you want to reduce the nesting inside that function, you can reverse the logic and `return` early like this:

```
function doSomethingSpecial(event) {
  if (!event.target.closest('button.special')) return;
  // do something
}

```

There’s a similar method to [`closest`](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest) called [`matches`](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches). But that will only work if the user clicks _directly_ on the element you’re interested in. If the element is nested within other elements, `matches` might not work, but `closest` will.

Like [Eric said](https://mastodon.social/@Meyerweb/111219441257570897):

> _Very_ nice.