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