Как найти ближайший элемент с классом

How can I find an element’s ancestor that is closest up the tree that has a particular class, in pure JavaScript? For example, in a tree like so:

<div class="far ancestor">
    <div class="near ancestor">
        <p>Where am I?</p>
    </div>
</div>

Then I want div.near.ancestor if I try this on the p and search for ancestor.

asked Mar 1, 2014 at 20:05

rvighne's user avatar

rvighnervighne

20.5k11 gold badges51 silver badges73 bronze badges

3

Update: Now supported in most major browsers

document.querySelector("p").closest(".near.ancestor")

Note that this can match selectors, not just classes

https://developer.mozilla.org/en-US/docs/Web/API/Element.closest


For legacy browsers that do not support closest() but have matches() one can build selector-matching similar to @rvighne’s class matching:

function findAncestor (el, sel) {
    while ((el = el.parentElement) && !((el.matches || el.matchesSelector).call(el,sel)));
    return el;
}

answered Nov 20, 2014 at 10:43

the8472's user avatar

the8472the8472

40.5k5 gold badges70 silver badges121 bronze badges

11

This does the trick:

function findAncestor (el, cls) {
    while ((el = el.parentElement) && !el.classList.contains(cls));
    return el;
}

The while loop waits until el has the desired class, and it sets el to el‘s parent every iteration so in the end, you have the ancestor with that class or null.

Here’s a fiddle, if anyone wants to improve it. It won’t work on old browsers (i.e. IE); see this compatibility table for classList. parentElement is used here because parentNode would involve more work to make sure that the node is an element.

answered Mar 1, 2014 at 20:05

rvighne's user avatar

rvighnervighne

20.5k11 gold badges51 silver badges73 bronze badges

12

Use element.closest()

https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

See this example DOM:

<article>
  <div id="div-01">Here is div-01
    <div id="div-02">Here is div-02
      <div id="div-03">Here is div-03</div>
    </div>
  </div>
</article>

This is how you would use element.closest:

var el = document.getElementById('div-03');

var r1 = el.closest("#div-02");  
// returns the element with the id=div-02

var r2 = el.closest("div div");  
// returns the closest ancestor which is a div in div, here is div-03 itself

var r3 = el.closest("article > div");  
// returns the closest ancestor which is a div and has a parent article, here is div-01

var r4 = el.closest(":not(div)");
// returns the closest ancestor which is not a div, here is the outmost article

answered Mar 27, 2018 at 14:54

simbro's user avatar

simbrosimbro

3,2947 gold badges34 silver badges46 bronze badges

4

Based on the the8472 answer and https://developer.mozilla.org/en-US/docs/Web/API/Element/matches here is cross-platform 2017 solution:

if (!Element.prototype.matches) {
    Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;
        };
}

function findAncestor(el, sel) {
    if (typeof el.closest === 'function') {
        return el.closest(sel) || null;
    }
    while (el) {
        if (el.matches(sel)) {
            return el;
        }
        el = el.parentElement;
    }
    return null;
}

Shai Coleman's user avatar

answered Mar 18, 2017 at 10:22

marverix's user avatar

marverixmarverix

7,1256 gold badges38 silver badges50 bronze badges

0

@rvighne solution works well, but as identified in the comments ParentElement and ClassList both have compatibility issues. To make it more compatible, I have used:

function findAncestor (el, cls) {
    while ((el = el.parentNode) && el.className.indexOf(cls) < 0);
    return el;
}
  • parentNode property instead of the parentElement property
  • indexOf method on the className property instead of the contains method on the classList property.

Of course, indexOf is simply looking for the presence of that string, it does not care if it is the whole string or not. So if you had another element with class ‘ancestor-type’ it would still return as having found ‘ancestor’, if this is a problem for you, perhaps you can use regexp to find an exact match.

answered Jun 18, 2017 at 13:02

Josh's user avatar

JoshJosh

3762 silver badges13 bronze badges

This solution should work for IE9 and up.

It’s like jQuery’s parents() method when you need to get a parent container which might be up a few levels from the given element, like finding the containing <form> of a clicked <button>. Looks through the parents until the matching selector is found, or until it reaches the <body>. Returns either the matching element or the <body>.

function parents(el, selector){
    var parent_container = el;
    do {
        parent_container = parent_container.parentNode;
    }
    while( !parent_container.matches(selector) && parent_container !== document.body );

    return parent_container;
}

answered Sep 23, 2020 at 19:28

James's user avatar

JamesJames

3894 silver badges10 bronze badges

Improve Article

Save Article

Like Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Like Article

    The task is to find the closest ancestor of the element of specific class with the help of pure Javascript. There are two approaches that are discussed below.

    Approach 1:  Use the Javascript selector to select the element. Use closest() method to get the closest parent with specific class. 
     

    Example: This example implements the above approach. 

    html

    <!DOCTYPE HTML> 

    <html

        <head>

            <title>

                Find closest ancestor element that has a

                specific class using pure JavaScript

            </title>       

        </head>

        <body style = "text-align:center;"

            <h1 style = "color:green;"

                GeeksforGeeks 

            </h1>

            <p id = "GFG_UP">

            </p>

            <div name = "parentDIV" class="parent">

              <div class="child">

              </div>

            </div>

            <button onclick = "myGFG()">

            click here

            </button>

            <p id = "GFG_DOWN">

            </p>

            <script>    

                var up = document.getElementById("GFG_UP");

                up.innerHTML = "Click on button to see result";

                var down = document.getElementById("GFG_DOWN");

                function myGFG() { 

                    var el = document.querySelector("div")

                    .closest(".parent")

                    down.innerHTML = "Element of name '"

                      + el.getAttribute("name")

                      + "' is the parent element of specific class.";

                }

            </script>

        </body

    </html>

    Output: 

    Approach 2 : Keep moving to the parents of the element until find the specific class. Use the element.className.indexof() method to select the element of particular class. 
     

    Example: This example implements the above approach. 

    html

    <!DOCTYPE HTML> 

    <html

        <head>

            <title>

                Find closest ancestor element that has a

                specific class using pure JavaScript

            </title>       

        </head>

        <body style = "text-align:center;"

            <h1 style = "color:green;"

                GeeksforGeeks 

            </h1>

            <p id = "GFG_UP">

            </p>

            <div name = "parentDIV" class="parent">

              <div class="child">

              </div>

            </div>

            <button onclick = "myGFG()">

            click here

            </button>

            <p id = "GFG_DOWN">

            </p>

            <script>    

                var up = document.getElementById("GFG_UP");

                up.innerHTML = "Click on button to see result";

                var down = document.getElementById("GFG_DOWN");

                function findParent(el, class) {

                    while ((el = el.parentNode) &&

                           el.className.indexOf(class) < 0);

                    return el;

                }

                function myGFG() { 

                    var el = document.querySelector(".child");

                    var el = findParent(el, 'parent');

                    down.innerHTML = "Element of name '"

                      + el.getAttribute("name")

                      + "' is the parent element of specific class.";

                }

            </script>

        </body

    </html>

    Output: 
     

    Last Updated :
    03 Mar, 2023

    Like Article

    Save Article

    В этой статье мы изучим методы JavaScript для поиска элементов в HTML-документе: querySelector, querySelectorAll, getElementById и другие. Кроме них рассмотрим ещё следующие: matches, contains и closest. Первые два из них могут быть полезны для выполнения различных проверок, а третий использоваться, когда нужно получить родительский элемент по CSS-селектору.

    Методы для выбора HTML-элементов

    Работа с веб-страницей так или иначе связана с манипулированием HTML-элементами. Но перед тем, как над ними выполнить некоторые действия (например, добавить стили), их сначала нужно получить.

    Выбор элементов в основном выполняется с помощью этих методов:

    • querySelector;
    • querySelectorAll.

    Они позволяют выполнить поиск HTML-элементов по CSS-селектору. При этом querySelector выбирает один элемент, а querySelectorAll – все.

    Кроме них имеются ещё:

    • getElementById;
    • getElementsByClassName;
    • getElementsByTagName;
    • getElementsByName.

    Но они сейчас применяются довольно редко. В основном используется либо querySelector, либо querySelectorAll.

    querySelectorAll

    Метод querySelectorAll применяется для выбора всех HTML-элементов, подходящих под указанный CSS-селектор. Он позволяет искать элементы как по всей странице, так и внутри определённого элемента:

    // выберем элементы по классу item во всем документе
    const items = document.querySelectorAll('.item');
    // выберем .btn внутри #slider
    const buttons = document.querySelector('#slider').querySelectorAll('.btn');

    Здесь на первой строчке мы нашли все элементы с классом item. На следующей строчке мы сначала выбрали элемент с id="slider", а затем в нём все HTML-элементы с классом btn.

    Метод querySelectorAll как вы уже догадались принимает в качестве аргумента CSS-селектор в формате строки, который соответственно и определяет искомые элементы. В качестве результата querySelectorAll возвращает объект класса NodeList. Он содержит все найденные элементы:

    Выбор HTML-элементов с помощью метода querySelectorAll в JavaScript

    Полученный набор представляет собой статическую коллекцию HTML-элементов. Статической она называется потому, что она не изменяется. Например, вы удалили элемент из HTML-документа, а в ней как был этот элемент, так он и остался. Чтобы обновить набор, querySelectorAll нужно вызвать заново:

    Статический набор HTML-элементов, выбранный с помощью querySelectorAll в JavaScript

    Узнать количество найденных элементов можно с помощью свойства length:

    // выберем элементы с атрибутом type="submit"
    const submits = document.querySelectorAll('[type="submit"]');
    // получим количество найденных элементов
    const countSubmits = submits.length;

    Обращение к определённому HTML-элементу коллекции выполняется также как к элементу массива, то есть по индексу. Индексы начинаются с 0:

    // получим первый элемент
    const elFirst = submits[0];
    // получим второй элемент
    const elSecond = submits[1];

    Здесь в качестве результата мы получаем HTML-элемент или undefined, если элемента с таким индексом в наборе NodeList нет.

    Перебор коллекции HTML-элементов

    Перебор NodeList обычно осуществляется с помощью forEach:

    // получим все <p> на странице
    const elsP = document.querySelectorAll('p');
    // переберём выбранные элементы
    elsP.forEach((el) => {
      // установим каждому элементу background-color="yellow"
      el.style.backgroundColor = 'yellow';
    });

    Также перебрать набор выбранных элементов можно с помощью цикла for или for...of:

    // получим все элементы p на странице
    const elsP = document.querySelectorAll('p');
    // for
    for (let i = 0, length = elsP.length; i < length; i++) {
      elsP[i].style.backgroundColor = 'yellow';
    }
    // for...of
    for (let el of elsP) {
      el.style.backgroundColor = 'yellow';
    }

    querySelector

    Метод querySelector также как и querySelectorAll выполняет поиск по CSS-селектору. Но в отличие от него, он ищет только один HTML-элемент:

    // ищем #title во всём документе
    const elTitle = document.querySelector('#title');
    // ищем footer в <body>
    const elFooter = document.body.querySelector('footer');

    На первой строчке мы выбираем HTML-элемент, имеющий в качестве id значение title. На второй мы ищем в <body> HTML-элемент по тегу footer.

    В качестве результата этот метод возвращает найденный HTML-элемент или null, если он не был найден.

    querySelector всегда возвращает один HTML-элемент, даже если под указанный CSS-селектор подходят несколько:

    <ul id="list">
      <li>First</li>
      <li>Second</li>
      <li>Third</li>
    </ul>
    <script>
      // выберем <li>, расположенный в #list
      const elFirst = document.querySelector('#list > li');
      elFirst.style.backgroundColor = 'yellow';
    </script>

    Задачу, которую решает querySelector можно выполнить через querySelectorAll:

    const elFirst = document.querySelectorAll('#list > li')[0];

    Но querySelector в отличие от querySelectorAll делает это намного быстрее, да и писать так проще. То есть querySelectorAll не возвращает как querySelector сразу же первый найденный элемент. Он сначала ищет все элементы, и только после того, как он это сделает, мы можем уже обратиться к первому HTML-элементу в этой коллекции.

    Обычно перед тем, как выполнить какие-то действия с найденным HTML-элементом необходимо сначала проверить, а действительно ли он был найден:

    const elModal = document.querySelector('.modal');
    // если элемент .modal найден, то ...
    if (elModal) {
      // переключим у elModal класс show
      elModal.classList.toggle('show');
    }

    Здесь мы сначала проверили существования HTML-элемента, и только потом выполнили над ним некоторые действия.

    Методы getElement(s)By* для выбора HTML-элементов

    Здесь мы рассмотрим методы, которые сейчас применяются довольно редко для поиска HTML-элементов. Но в некоторых случаях они могут быть очень полезны. Это:

    • getElementById – получает один элемент по id;
    • getElementsByClassName – позволяет найти все элементы с указанным классом или классами;
    • getElementsByTagName – выбирает элементы по тегу;
    • getElementsByName – получает все элементы с указанным значением атрибута name.

    1. Метод getElementById позволяет найти HTML-элемент на странице по значению id:

    <div id="comments">...</div>
    ...
    <script>
      // получим HTMLElement и сохраним его в переменную elComments
      const elComments = document.getElementById('comments');
    </script>

    В качестве результата getElementById возвращает объект класса HTMLElement или значение null, если элемент не был найден. Этот метод имеется только у объекта document.

    Указывать значение id необходимо с учётом регистра. Так например, document.getElementById('aside') и document.getElementById('ASIDE') ищут элементы с разным id.

    Обратите внимание, что в соответствии со стандартом в документе не может быть несколько тегов с одинаковым id, так как значение идентификатора на странице должно быть уникальным.

    Тем не менее, если вы допустили ошибку и в документе существуют несколько элементов с одинаковым id, то метод getElementById более вероятно вернёт первый элемент, который он встретит в DOM. Но на это полагаться нельзя, так как такое поведение не прописано в стандарте.

    То, что делает getElementById можно очень просто решить посредством querySelector:

    // получим элемент #title
    const elTitle = document.getElementById('title');
    // получим элемента #title, используя querySelector
    const elTitleSame = document.querySelector('#nav');

    Кстати, оба этих метода возвращают в качестве результата один и тот же результат. Это либо HTML-элемент (экземпляр класса HTMLElement) или null, если элемент не найден.

    2. Метод getElementsByClassName позволяет найти все элементы с заданным классом или классами. Его можно применить для поиска элементов как во всём документе, так и внутри указанного. В первом случае его нужно будет вызывать как метод объекта document, а во втором – как метод соответствующего HTML-элемента:

    // найдем элементы с классом control в документе
    const elsControl = document.getElementsByClassName('control');
    // выберем элементы внутри другого элемента, в данном случае внутри формы с id="myform"
    const elsFormControl = document.forms.myform.getElementsByClassName('form-control');

    В качестве результата он возвращает живую HTML-коллекцию найденных элементов. Чем живая коллекция отличается от статической мы рассмотрим ниже.

    Здесь мы сохранили найденные элементы в переменные elsControl и elsFormControl. В первой переменной будет находиться HTMLCollection, содержащая элементы с классом control. Во второй – набор элементов с классом form-control, находящиеся в форме с id="myform". Для получения этой формы мы использовали document.forms.myform.

    Метод getElementsByClassName позволяет искать элементы не только по одному классу, но и сразу по нескольким, которые должны присутствовать у элемента:

    // выберем элементы .btn.btn-danger
    const elsBtn = document.getElementsByClassName('btn btn-danger');

    На querySelectorAll этот пример решается так:

    const elsBtn = document.querySelectorAll('.btn.btn-danger');

    3. Метод getElementsByTagName предназначен для получения коллекции элементов по имени тега:

    // найдем все <a> в документе
    const anchors = document.getElementsByTagName('a');
    // найдем все >li> внутри #list
    const elsLi = document.getElementById('list').getElementsByTagName('li');

    На первой строчке мы выбрали все <a> в документе и присвоили полученную HTMLCollection переменной anchors. На второй – мы сначала получили #list, а затем в нём нашли все <li>.

    Задачу по выбору элементов внутри другого элемента с помощью querySelectorAll выполняется намного проще:

    const elsLi = document.querySelectorAll('#list li');

    Для выбора всех элементов можно использовать символ *:

    // выберем все элементы в <body>
    const els = document.body.getElementsByTagName('*');

    4. В JavaScript getElementsByName можно использовать для выбора элементов, имеющих определенное значение атрибута name:

    // получим все элементы с name="phone"
    const elsPhone = document.getElementsByName('phone');

    Через querySelectorAll это выполняется так:

    const elsPhone = document.querySelectorAll('[name="phone"]');

    getElementsBy* и живые HTML-коллекции

    В JavaScript getElementsByTagName, getElementsByClassName и getElementsByName в отличие от других методов (например, querySelectorAll) возвращают живую коллекцию HTML-элементов (на английском live HTMLCollection). То есть коллекцию содержимое которой автоматически обновляется при изменении DOM. Для наглядности рассмотрим следующий пример.

    Например, на странице изначально имеется два <li>. Выберем их с помощью getElementsByTagName и сохраним полученную HTMLCollection в переменную els. Теперь с помощью els мы можем получить эту коллекцию. Сейчас в ней два <li>. Затем через 5 секунд, используя setTimeout добавим ещё один <li>. Если сейчас мы обратимся к переменной els, то увидим, что в ней уже находятся три <li>:

    <ul>
      <li>One</li>
      <li>Two</li>
    </ul>
    
    <script>
    // получим живую коллекцию <li>
    const els = document.getElementsByTagName('li');
    // выведем количество <li> в консоль
    console.log(`Количество <li>: ${els.length}`); // 2
    // через 5 секунд добавим ещё один <li>
    setTimeout(() => {
      // вставим на страницу новый <li>
      document.querySelector('ul').insertAdjacentHTML('beforeend', '<li>Three</li>');
    // выведем количество <li> в консоль
      console.log(`Количество <li>: ${els.length}`); // 3
    }, 5000);
    </script>

    Живая коллекция элементов, полученная с помощью getElementsByTagName, обновляется при изменении DOM

    Как вы видите, здесь полученная коллекция является живой, то есть она может автоматически измениться. В ней сначала было два <li>. Но после того, как мы на страницу добавили ещё один подходящий элемент, в ней их стало уже три.

    Если в коде приведённом выше заменить выбор элементов на querySelectorAll, то мы увидим, что в ней находится статическая (не живая) коллекция элементов:

    // получим статическую коллекцию
    const els = document.querySelectorAll('li'); <li>

    Статическая коллекция элементов, полученная с помощью querySelectorAll, не обновляется при изменении DOM

    Как вы видите количество элементов в коллекции не изменилось. Чтобы после изменения DOM получить актуальную коллекцию элементов, их нужно просто выбрать заново посредством querySelectorAll:

    <ul>
      <li>One</li>
      <li>Two</li>
    </ul>
    
    <script>
    // получим статическую коллекцию <li>
    let els = document.querySelectorAll('li');
    // выведем количество <li> в консоль
    console.log(`Количество <li>: ${els.length}`); // 2
    // через 5 секунд добавим ещё один <li>
    setTimeout(() => {
      // вставим на страницу новый <li>
      document.querySelector('ul').insertAdjacentHTML('beforeend', '<li>Three</li>');
      // получим заново статическую коллекцию <li>
      els = document.querySelectorAll('li');
      // выведем количество <li> в консоль
      console.log(`Количество <li>: ${els.length}`); // 3
    }, 5000);
    </script>

    Таким образом в JavaScript насчитывается 6 основных методов для выбора HTML-элементов на странице. По чему они ищут и что они возвращают приведено на следующем рисунке:

    Методы JavaScript для выбора HTML-элементов на странице, приведены сведения по признаку на основании которого они ищут и тому что возвращают в качестве результата

    Экземпляры класса HTMLCollection не имеют в прототипе метод forEach. Поэтому если вы хотите использовать этот метод для перебора такой коллекции, её необходимо преобразовать в массив:

    const items = document.getElementsByClassName('item');
    [...items].forEach((el) => {
      console.log(el);
    });

    matches, closest и contains

    В JavaScript имеются очень полезные методы:

    • matches – позволяет проверить соответствует ли HTML-элемент указанному CSS-селектору;
    • closest – позволяет найти для HTML-элемента его ближайшего предка, подходящего под указанный CSS-селектор (поиск начинается с самого элемента);
    • contains – позволяет проверить содержит ли данный узел другой в качестве потомка (проверка начинается с самого этого узла).

    1. Метод matches ничего не выбирает, но он является очень полезным, так как позволяет проверить HTML-элемент на соответствие CSS-селектору. Он возвращает true, если элемент ему соответствует, иначе false.

    // выберем HTML элемент, имеющий атрибут data-target="slider"
    const elSlider = document.querySelector('[data-target="slider"]');
    // проверим соответствует ли он CSS селектору 'div'
    const result = element.matches('div');

    Пример, в котором выберем все <li>, расположенные внутри #questions, а затем удалим те из них, которые соответствуют селектору .answered:

    // выберем все <li> в #questions
    const els = document.querySelectorAll('#questions > li');
    // переберём выбранные элементы
    els.forEach((el) => {
      // если элемент соответствует селектору .answered, то ...
      if (el.matches('.answered')) {
        // удалим элемент
        el.remove();
      }
    });

    В этом примере проверим каждый <li> на соответствие селектору active. Выведем в консоль каждый такой элемент:

    <ul>
      <li>One</li>
      <li class="active">Two</li>
      <li>Three</li>
    </ul>
    
    <script>
      document.querySelectorAll('li').forEach((el) => {
        if (el.matches('.active')) {
          console.log(el);
        }
      });
      // li.active
    </script>

    Ранее, в «старых» браузерах данный метод имел название matchesSelector, а также поддерживался с использованием префиксов. Если вам нужна поддержка таких браузеров, то можно использовать следующий полифилл:

    if (!Element.prototype.matches) {
      Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.webkitMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector;
    }

    2. Метод closest очень часто используется в коде. Он позволяет найти ближайшего предка, подходящего под указанный CSS-селектор. При этом поиск начинается с самого элемента, для которого данный метод вызывается. Если этот элемент будет ему соответствовать, то closest вернёт его.

    <div class="level-1">
      <div class="level-2">
        <div class="level-3"></div>
      </div>
    </div>
    
    <script>
      const el = document.querySelector('.level-3');
      const elAncestor = el.closest('.level-1');
      console.log(elAncestor);
    </script>

    Здесь мы сначала выбираем HTML-элемент .level-3 и присваиваем его переменной el. Далее мы пытаемся среди предков этого элемента включая его сам найти такой, который отвечает заданному CSS-селектору, в данном случае .level-1.

    Начинается поиск всегда с самого этого элемента. В данном случае он не подходит под указанный селектор. Следовательно, этот метод переходит к его родителю. Он тоже не отвечает этому CSS-селектору. Значит, closest переходит дальше, то есть уже к его родителю. Этот элемент подходит под указанный селектор. Поэтому поиск прекращается и этот метод возвращает его в качестве результата.

    Метод closest возвращает null, когда он дошёл бы конца иерархии и не нашёл элемент отвечающий указанному селектору. То есть, если такого элемента нет среди предков.

    В этом примере найдем с помощью closest для .active его ближайшего родителя, отвечающего CSS-селектору #list > li:

    <ul id="list">
      <li>One</li>
      <li>
        Two
        <ul>
          <li>Four</li>
          <li class="active">Five</li>
        </ul>
      </li>
      <li>Three</li>
    </ul>
    <script>
      const elActive = document.querySelector('.active');
      const elClosest = elActive.closest('#list > li');
      elClosest.style.backgroundColor = 'yellow';
    </script>

    В JavaScript closest очень часто используется в обработчиках событий. Это связано с тем, чтобы события всплывают и нам нужно, например, узнать кликнул ли пользователь в рамках какого-то элемента:

    document.addEventListener('click', (e) => {
      if (e.closest.matches('.btn__action')) {
        // пользователь кликнул внутри .btn__action
      }
    });

    3. Метод contains позволяет проверить содержит ли некоторый узел другой в качестве потомка. При этом проверка начинается с самого этого узла, для которого этот метод вызывается. Если узел соответствует тому для которого мы вызываем данный метод или является его потомком, то contains в качестве результата возвращает логическое значение true. В противном случае false:

    <div id="div-1">
      <div id="div-2">
        <div id="div-3">...</div>
      </div>
    </div>
    <div id="div-4">...</div>
    
    <script>
      const elDiv1 = document.querySelector('#div-1');
      elDiv1.contains(elDiv1); // true
      const elDiv3 = document.querySelector('#div-3');
      elDiv1.contains(elDiv3); // true
      const elDiv4 = document.querySelector('#div-4');
      elDiv1.contains(elDiv4); // false
    </script>

    Здесь выражение elDiv1.contains(elDiv1) возвращает true, так как проверка начинается с самого элемента. Это выражение elDiv1.contains(elDiv3) тоже возвращает true, так как elDiv3 находится внутри elDiv1. А вот elDiv1.contains(elDiv4) в качестве результата возвращает false, так как elDiv4 не находится внутри elDiv1.

    В этом примере проверим с помощью contains содержит ли <p> другие узлы в качестве потомка:

    <h1>Tag b</h1>
    <p>This is <b>tag b</b>.</p>
    
    <script>
      const elP = document.querySelector('p');
      const elB = document.querySelector('b');
      const textNode = elB.firstChild;
      const elH1 = document.querySelector('h1');
      elP.contains(elP); // true
      elP.contains(elB); // true
      elP.contains(elH1); // false
      elP.contains(textNode); // true
    </script>

    Метод contains позволяет проверить является ли потомком не только узел-элемент, но и любой другой узел. Например, узнаем является ли потомком elDiv1 указанный текстовый узел:

    const elDiv1 = document.querySelector('#div-1');
    const textNode = document.querySelector('#div-3').firstChild;
    elDiv1.contains(textNode); // true

    Задачи

    1. Узнать количество элементов с атрибутом data-toggle="modal" на странице:

    const count = document.querySelectorAll('[data-toggle="modal"]').length;
    console.log(count);

    2. Найти все элементы <a> с классом nav внутри элемента <ul> :

    const anchors = document.querySelectorAll('ul.nav a');

    3. Получить элемент по id, значение которого равно pagetitle:

    var pagetitle = document.querySelector('#pagetitle');

    4. Выполнить поиск элемента по классу nav:

    var el = document.querySelector('.nav');

    5. Найти элемент <h3>, находящийся в теге <div> с классом comments, который в свою очередь расположен в <main>:

    var header = document.querySelector('main div.comments h3');

    6. Имеется страница. В ней следует выбрать:

    • последний элемент с классом article, расположенный в <main> (решение);
    • все элементы .section, находящиеся в .aside кроме 2 второго (решение);
    • элемент <nav> расположенный после <header> (решение).

    Contents:

    • .closest( selector )

      • .closest( selector )
      • .closest( selector [, context ] )
      • .closest( selection )
      • .closest( element )
    • .closest( selectors [, context ] )

      • .closest( selectors [, context ] )

    .closest( selector )Returns: jQuery

    Description: For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.

    • version added: 1.3.closest( selector )

      • selector

        A string containing a selector expression to match elements against.

    • version added: 1.4.closest( selector [, context ] )

      • selector

        A string containing a selector expression to match elements against.

      • context

        A DOM element within which a matching element may be found.

    • version added: 1.6.closest( selection )

      • selection

        A jQuery object to match elements against.

    • version added: 1.6.closest( element )

      • element

        An element to match elements against.

    Given a jQuery object that represents a set of DOM elements, the .closest() method searches through these elements and their ancestors in the DOM tree and constructs a new jQuery object from the matching elements. The .parents() and .closest() methods are similar in that they both traverse up the DOM tree. The differences between the two, though subtle, are significant:

    .closest()

    .parents()

    Begins with the current element Begins with the parent element
    Travels up the DOM tree until it finds a match for the supplied selector Travels up the DOM tree to the document’s root element, adding each ancestor element to a temporary collection; it then filters that collection based on a selector if one is supplied
    The returned jQuery object contains zero or one element for each element in the original set, in document order The returned jQuery object contains zero or more elements for each element in the original set, in reverse document order

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    <ul id="one" class="level-1">

    <li class="item-i">I</li>

    <li id="ii" class="item-ii">II

    <li class="item-a">A</li>

    <li class="item-1">1</li>

    <li class="item-2">2</li>

    <li class="item-3">3</li>

    <li class="item-c">C</li>

    <li class="item-iii">III</li>

    Suppose we perform a search for <ul> elements starting at item A:

    1

    2

    3

    .css( "background-color", "red" );

    This will change the color of the level-2 <ul>, since it is the first encountered when traveling up the DOM tree.

    Suppose we search for an <li> element instead:

    1

    2

    3

    .css( "background-color", "red" );

    This will change the color of list item A. The .closest() method begins its search with the element itself before progressing up the DOM tree, and stops when item A matches the selector.

    We can pass in a DOM element as the context within which to search for the closest element.

    1

    2

    3

    4

    5

    6

    7

    var listItemII = document.getElementById( "ii" );

    .closest( "ul", listItemII )

    .css( "background-color", "red" );

    .closest( "#one", listItemII )

    .css( "background-color", "green" );

    This will change the color of the level-2 <ul>, because it is both the first <ul> ancestor of list item A and a descendant of list item II. It will not change the color of the level-1 <ul>, however, because it is not a descendant of list item II.

    Examples:

    Show how event delegation can be done with closest. The closest list element toggles a yellow background when it or its descendent is clicked.

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    <title>closest demo</title>

    <script src="https://code.jquery.com/jquery-3.7.0.js"></script>

    <li><b>Click me!</b></li>

    <li>You can also <b>Click me!</b></li>

    $( document ).on( "click", function( event ) {

    $( event.target ).closest( "li" ).toggleClass( "highlight" );

    Demo:

    Pass a jQuery object to closest. The closest list element toggles a yellow background when it or its descendent is clicked.

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    <title>closest demo</title>

    <script src="https://code.jquery.com/jquery-3.7.0.js"></script>

    <li><b>Click me!</b></li>

    <li>You can also <b>Click me!</b></li>

    var listElements = $( "li" ).css( "color", "blue" );

    $( document ).on( "click", function( event ) {

    $( event.target ).closest( listElements ).toggleClass( "highlight" );

    Demo:

    .closest( selectors [, context ] )Returns: Arrayversion deprecated: 1.7, removed: 1.8

    Description: Get an array of all the elements and selectors matched against the current element up through the DOM tree.

    • version added: 1.4.closest( selectors [, context ] )

      • selectors

        An array or string containing a selector expression to match elements against (can also be a jQuery object).

      • context

        A DOM element within which a matching element may be found.

    This signature (only!) is deprecated as of jQuery 1.7 and removed in jQuery 1.8. It was primarily meant to be used internally or by plugin authors.

    На этом занятии
    рассмотрим методы поиска произвольных элементов в HTML-документе. Рассмотренные
    свойства на предыдущем занятии по навигации DOM-дереву хороши
    если элементы расположены рядом. Но что делать, если требуемый объект может
    находиться в самых разных местах. Как его искать? Вот об этом и пойдет сейчас
    речь.

    В самом простом
    случае мы можем у любого тега HTML-документа прописать атрибут id с некоторым уникальным
    значением. Например:

    <!DOCTYPE html>
    <html>
    <head>
        <title>Уроки по JavaScript</title>
    </head>
    <body>
    <div id="div_id">
        <p>Текст внутри блока div
    </div>
    <script>
    </script>
    </body>
    </html>

    Здесь у нас тег div имеет атрибут id со значением div_id. Мы это
    значение придумываем сами, главное, чтобы оно было уникальным в пределах HTML-страницы.
    Теперь можно получить этот элемент div по этому id, где бы он ни
    находился в DOM-дереве. Для
    этого используется метод getElementById объекта document:

    let divElem = document.getElementById('div_id');
    console.log( divElem );

    Мы в методе getElementById в качестве
    аргумента указываем строку со значением атрибута id и на выходе
    получаем ссылку на этот элемент.

    Или же можем
    получить доступ к этому элементу напрямую через глобальную переменную div_id, которая
    автоматически создается браузером при формировании DOM-дерева:

    Но этот второй
    способ имеет одну уязвимость: если мы создадим в скрипте переменную с таким же
    именем, то прежнее значение div_id будет затерто:

    let div_id = "не тег div";

    так что этот
    подход следует применять с большой осторожностью, лучше использовать метод getElementById.

    В стандарте ES6+ появился
    новый метод поиска элементов querySelectorAll, который возвращает список
    элементов, удовлетворяющих CSS-селектору, который мы в нем указываем.

    Например,
    добавим в HTML-документ вот
    такой маркированный список:

    <ul>
    <li>Солнце
    <li>Меркурий
    <li>Венера
    <li>Земля
    <li>Марс
    </ul>

    и вот такой
    нумерованный список:

    <p>Звезды:
    <ol>
    <li>Сириус
    <li>Альдебаран
    <li>Капелла
    <li>Ригель
    </ol>

    И теперь хотим
    выбрать все теги <li>, но только у маркированного списка.
    Для этого запишем метод querySelectorAll с таким CSS-селектором:

    let list = document.querySelectorAll("ul > li");
    for(let val of list)
       console.log(val);

    Как видите, у
    нас были выбраны элементы только маркированного списка. Мало того, в методе querySelectorAll
    можно использовать псевдоклассы для указания более сложных CSS-селекторов,
    например, так:

    let list = document.querySelectorAll("ul > li:first-child");

    тогда мы увидим
    только первый элемент списка. И так далее. С помощью querySelectorAll можно довольно
    просто выбрать нужные элементы и далее производить с ними необходимые действия.

    Если же нам
    нужно по определенному CSS-селектору найти только первый
    подходящий элемент, то для этого применяется другой метод querySelector:

    let element = document.querySelector("ol > li");
    console.log(element);

    Здесь мы из
    нумерованного списка выбрали первый тег <li>. Конечно,
    здесь можно было бы использовать и предыдущий метод querySelectorAll, а затем,
    взять из списка только первый:

    let element = document.querySelectorAll("ol > li")[0];

    но это будет
    работать медленнее, так как все равно сначала будут находиться все подходящие
    элементы, а затем, браться первый. Поэтому, если нужно найти только первый
    подходящий, то следует использовать querySelector.

    Следующий метод matches
    позволяет определить: подходит ли данный элемент под указанный CSS-селектор или
    нет. Если подходит, то возвращает true, иначе – false. Например,
    создадим такое оглавление:

    <h1>О звездах</h1>
    <div class="content-table">
           <ul class="stars-list">
                  <li class="star">О сириусе</li>
                  <li class="star">Об альдебаране</li>
                  <li class="contact">Обратная связь</li>
           </ul>
    </div>

    И мы, перебирая
    список пунктов меню, хотим выбрать только те элементы, у которых class равен star. Это можно сделать так:

    let list = document.querySelectorAll("ul.stars-list > li");
     
    for(let item of list) {
         if(item.matches("li.star")) console.log(item);
    }

    Обратите
    внимание, что метод matches относится к объекту DOM, а не к document. Что, в
    общем-то логично, так как нам нужно проверить конкретный элемент на
    соответствие CSS-селектора. В
    результате, в консоле мы увидим первые два элемента:

    <li class="star">О сириусе</li>
    <li class="star">Об альдебаране</li>

    Следующий метод
    elem.closest(css) ищет ближайшего предка, который соответствует CSS-селектору.
    Сам элемент также включается в поиск. Метод возвращает либо предка, либо null,
    если такой элемент не найден. Например:

    let li = document.querySelector("li.star");
    console.log(li.closest('.stars-list'));
    console.log(li.closest('.content-table'));
    console.log(li.closest('h1')); // null

    Сначала мы
    выбираем первый элемент li пункта меню.
    Затем, с помощью метода closest ищем ближайшего родителя с
    классом stars-list. Находится
    список ul. Далее, ищем
    родителя с классом content-table. Находим блок div. Наконец,
    пытаемся найти родителя с тегом h1. Но его нет, так как h1 в документе не
    является родителем для объекта li. Получаем значение null.

    В старых версиях
    языка JavaScript (стандарта ES5-) существуют
    следующие методы для поиска элементов:

    • elem.getElementsByTagName(tag)
      ищет элементы с указанным тегом и возвращает их коллекцию. Указав “*”
      вместо тега, можно получить всех потомков.

    • elem.getElementsByClassName(className)
      возвращает элементы, которые имеют указанный CSS-класс.

    • document.getElementsByName(name)
      возвращает элементы с заданным атрибутом name. (Используется очень редко).

    Применение этих
    методов очевидно и я привел их здесь лишь для полноты картины, так как вы
    можете их встретить в старых скриптах. Вместо всех этих методов теперь
    используются методы querySelector и querySelectorAll. Они дают
    гораздо больше возможностей, чем старые методы.

    Однако, между методами
    getElementsBy* и querySelector, querySelectorAll есть одно важное
    отличие: методы getElementsBy* возвращают, так называемую, «живую»
    коллекцию, то есть, они всегда отражают текущее состояние документа и автоматически
    обновляются при его изменении. Например, при загрузке и отображении такого HTML-документа:

    <!DOCTYPE html>
    <html>
    <head>
        <title>Уроки по JavaScript</title>
    </head>
    <body>
    <h1>О звездах</h1>
    <h2>Об альдебаране</h2>
    <script>
        let list = document.getElementsByTagName("h2");
    </script>
    <h2>О ригеле</h2>
    <script>
        for(let item of list) console.log(item);
    </script>
    </body>
    </html>

    Мы в консоле
    увидим список из двух тегов h2, хотя когда этот список формировался в
    HTML-документе был
    всего один тег h2. Второй добавился позже, автоматически. Но, если
    мы будем получать список этих элементов с помощью метода querySelectorAll:

       let list = document.querySelectorAll("h2");

    то увидим только
    один тег h2. Так как здесь
    делается как бы снимок коллекции на текущий момент состояния HTML-документа и
    после этого никак не меняется. Вот этот момент при работе с этими методами
    следует иметь в виду.

    Ну и в
    заключение этого занятия отметим еще один полезный метод

    который
    возвращает значение true, если elemB является дочерним по отношению к elemA.
    И false в противном
    случае. Например, вот в этом документе:

    <div class="content-table">
        <ul class="stars-list">
           <li class="star">О сириусе</li>
           <li class="star">Об альдебаране</li>
           <li class="contact">Обратная связь</li>
        </ul>
    </div>

    Можно проверить:
    имеется ли список внутри тега div:

    let div = document.querySelector("div.content-table");
    let ul = document.querySelector("ul.stars-list");
     
    if(div.contains(ul)) 
         console.log("ul внутри div");

    Вот такие
    основные методы поиска элементов в DOM-дереве есть в JavaScript.

    Видео по теме

    Добавить комментарий