Методы поиска элементов в DOM

На этом занятии рассмотрим методы поиска произвольных элементов в 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-дерева:

console.log( div_id );

Но этот второй способ имеет одну уязвимость: если мы создадим в скрипте переменную с таким же именем, то прежнее значение 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-документа и после этого никак не меняется. Вот этот момент при работе с этими методами следует иметь в виду.

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

elemA.contains(elemB)

который возвращает значение 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.

Видео по теме