Signatur
Beschreibung
closest() prüft zuerst das Element selbst und steigt dann Schritt für Schritt über parentElement nach oben, bis ein Element dem angegebenen CSS-Selektor entspricht. Das erste passende Element wird zurückgegeben. Wird bis zur Wurzel keins gefunden, ist das Ergebnis null.
Am häufigsten braucht man die Methode bei der Ereignisdelegation (Event Delegation): Ein einziger Listener sitzt auf einem Container, und mit event.target.closest('button[data-action]') findet man heraus, welches logische Element angeklickt wurde, auch wenn der Klick auf einem verschachtelten Kindelement wie einem <span> oder <svg> gelandet ist.
Die Methode ist quasi das Gegenstück zu querySelector(): querySelector() sucht abwärts in den Nachfahren, closest() sucht aufwärts in den Vorfahren. Der Selektor wird genauso ausgewertet wie bei matches() und darf deshalb komplexe Selektoren enthalten, etwa Kombinatoren, Attributselektoren und Pseudoklassen wie :not() oder :has().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $selectors Pflicht | string | Ein gültiger CSS-Selektor oder eine kommagetrennte Selektorliste, z. B. '.card, [data-panel]'. |
Rückgabewert
null.Beispiele
Grundlegende Suche nach Vorfahren
// HTML:
// <article class="post" id="p1">
// <div class="content">
// <a href="#" id="link">Weiterlesen</a>
// </div>
// </article>
const link = document.getElementById('link');
console.log(link.closest('.post').id); // nächstes Artikel-Element
console.log(link.closest('div').className); // nächster <div>
console.log(link.closest('a') === link); // Element selbst zählt mit
console.log(link.closest('.sidebar')); // nicht vorhanden
Ereignisdelegation in einer Aufgabenliste
// HTML:
// <ul id="todos">
// <li data-id="1">Einkaufen <button data-action="delete"><span>🗑</span></button></li>
// <li data-id="2">Sport <button data-action="delete"><span>🗑</span></button></li>
// </ul>
const list = document.getElementById('todos');
list.addEventListener('click', (event) => {
// Klick kann auf dem <span> landen, deshalb closest()
const button = event.target.closest('button[data-action="delete"]');
if (!button || !list.contains(button)) return;
const item = button.closest('li');
console.log(`Lösche Eintrag ${item.dataset.id}`);
item.remove();
});
Klick außerhalb schließt ein Dropdown
const dropdown = document.querySelector('.dropdown');
const handleOutsideClick = (event) => {
// Ist der Klick innerhalb eines Dropdowns gelandet?
if (!event.target.closest('.dropdown')) {
dropdown.classList.remove('open');
console.log('Dropdown geschlossen');
}
};
document.addEventListener('click', handleOutsideClick);
Selektorliste und ungültiger Selektor
const input = document.querySelector('input[name="email"]');
// Nächster Container, egal ob Formular oder Dialog
const container = input.closest('form, dialog, [role="dialog"]');
console.log(container?.tagName ?? 'kein Container');
try {
input.closest('::invalid[');
} catch (err) {
console.log(err.name);
}
// Wichtig · Fallstricke
- Das Element selbst wird mitgeprüft. Wenn nur Vorfahren gesucht werden sollen, verwende
el.parentElement?.closest(sel). - Ist
selectorskein gültiger CSS-Selektor, wirft die Methode eineSyntaxError-DOMException. - Prüfe das Ergebnis immer auf
null, bevor du darauf zugreifst. Optional Chaining (?.) hilft dabei. - Bei der Ereignisdelegation kann
closest()auch ein Element außerhalb des Containers finden, auf dem der Listener sitzt. Das passiert, wenn der Container selbst in einem passenden Element verschachtelt ist. Prüfe das zusätzlich mitcontainer.contains(result). event.targetkann ein Textknoten oder ein Nicht-Element sein, zum Beispieldocumentoderwindowbei eigenen Ereignissen. Diese haben keinclosest(). Prüfe in diesem Fall vorher mitevent.target instanceof Element.- Die Suche endet an der Grenze eines Shadow Root. Elemente im Light DOM des Hosts werden nicht gefunden.
- Die Methode wird in allen modernen Browsern unterstützt. Der Internet Explorer kennt sie nicht; dort war ein Polyfill mit
msMatchesSelectorund einer Schleife überparentElementnötig. - Zur Laufzeit: Die Suche kostet O(Tiefe des DOM-Baums) und ist in der Praxis sehr schnell. Selektoren mit
:has()können aber teuer werden.