Селекторы HTML элементов в JavaScrip
Манипулирование деревом DOM
, это альфа и омега любого фронтенд-разработчика, а это не возможно без селекторов позволяющих находить HTML
элементы. Давайте подробно разберёмся как они работают.
Основных методов селекторов в JavaScript всего 2 и оба они являются методами классов Document
и Element
:
querySelector()
— принимает строку с селектором в качестве аргумента и возвращает первое совпадение с ним илиnull
, если ничего не найдено.querySelectorAll()
— точно так-же принимает аргументом, строку с селектором и возвращает все найденные элементы в виде массива элементовNodeList
, с которым можно работать в циклеfor
илиfor/of
. Если элементы не будут найдены вернёт пустой массивNodeList
.Именно этим двум методам стоит отдавать предпочтение в своём коде, хотя есть и другие альтернативы. Селекторы используемые в методах, взяты прямиком из
CSS
, следовательно фронтендерам они будут уже знакомы, а это нехилый плюс. Ниже будут примеры кода, для которых был набросан простенькаяHTMLка
:
Селекторы
Ссылка на раздел
Посмотрим на разницу работы методов с одинаковыми селекторами:
const one = document.querySelector('div'), // Содержит ссылку на
all = document.querySelectorAll('div') // Содержит NodeList со всеми 4-мя дивами что есть в нашем документе
Для поиска можно использовать сразу несколько селекторов отделяя друг от друга их запятыми:
const one = document.querySelector('#elem_2, a'), // Содержит элемент ссылки , так как элемент по этому селектору встречается раньше
all = document.querySelectorAll('a, #elem_2') // Содержит NodeList с 2 элементами: 2 ссылки и
Тогда методы вернут элементы которые подходят хотя-бы под один из указанных селекторов. Благодаря CSS
селекторам можно найти элементы по:
ID;
Классу;
Атрибутам;
Позиции.
Как упоминалось выше, методы селекторы имеются у двух классов Document
и Element
и их работа отличается тем что в случае с классом Element
, выбираться будут только потомки элемента у которого был вызван метод:
const someDiv = document.querySelector('[id="elem_3"]'),
searchInElem = someDiv.querySelector('a'), // Содержит
searchInDocument = document.querySelector('a') // // Содержит
CSS
селекторы по псевдолементам:
::first-line
;
::first-letter
. не будут работать с querySelectot()
и querySelectotAll()
, так как предназначены для работы с текстовыми узлами, а не с HTML
элементами. Некоторые браузеры не реализуют работу с псевдоклассами CSS
:
:link
;
:visited
. Делается это из соображений безопасности, так как через них можно следить за хронологий посещений пользователя.
Полезным в работе может оказаться метод matches()
, класса Element
, который принимает селектор в качестве аргумента и проверяет соответствует ли элемент данному селектору, если да, то возвращает true
.
const someDiv = document.querySelector('#elem_3')
let isDiv = someDiv.matches('div'), // true
isLink = someDiv.matches('a') // false
Поиск родительских элементов по селектору
Если метод querySelector()
класса Element
ищет элементы в направлении сверху вниз и работает только с дочерними элементами, то метод closets()
, позволяет проделать ту-же операцию в обратном направлении и предназначен для работы уже предками элемента. Он точно так-же принимает селектор в качестве единственного аргумента и возвращает найденного родителя, а если по указанному селектору ничего найти не удастся то вернёт null
.
const someLink = document.querySelector('#link_2'),
linkParentDiv = someLink.closest('div') //
Устаревшие методы селекторы
Есть ряд методов класса Document
, использование которых не запрещено, но все-же лучше вместо них применять querySelectot()
или querySelectotAll()
, чтобы не усложнять код:
getElementById()
— находит элемент по ID
;
getElementByTagName()
— находит элемент по имени тэга;
getElementByName()
— находит элемент по атрибуту name
;
getElementByClassName()
— находит элемент по имени класса.
Два метода из данного списка:
getElementByTagName()
;
getElementByClassName()
. Реализованы так-же и в классе Element
и позволяют искать его потомков. Все они за исключением getElementById()
возвращают NodeList
. getElementById()
возвращает объект Element
.
Реализованы так-же и в классе Element
и позволяют искать его потомков. Все они за исключением getElementById()
возвращают NodeList
. getElementById()
возвращает объект Element
.
NodeList
возвращаемый устаревшими методами, является активным, то есть изменяет свою длину при появлении/удалении новых элементов, соответствующих указанному селектору .
const elemById = document.getElementById('elem_1'), //
tags = document.getElementsByTagName('a'), // HTMLCollection со всеми ссылкам
elemByName = document.getElementsByName('div_2'), // NodeList с
elemByClass = document.getElementsByClassName('link_wrap') // HTMLCollection с
Доступ к элементам через свойства
В классе Document
реализован ряд свойств для быстрого доступа к элементам HTML
:
images
— все картинки;
forms
— все формы;
links
— все ссылки с атрибутом href
;
all
— содержит все HTML
элементы, документа. Наиболее устаревшее из всех свойств и его не рекомендуется использовать. Все они ссылаются на объект HTMLCollection
, который похож на NodeList
, но в качестве индекса может быть использован ID
элемента или его имя (атрибут name
):
document.links.link_1 //
Все данные свойства являются устаревшими и не рекомендованы к использованию. Опять-же querySelectot()
иquerySelectotAll()
, наше всё!
Движение по документу относительно элемента
Мы можем использовать элемент как точку отсчёта и двигаться по документу относительно него в любых, направлениях. Для этого в классе Element
имеются свойства часть из которых работает только:
parentNode
— родительский элемент;
children
— потомков элемента;
firstElementChild
— первый дочерний элемент. Равен null
, если дочерние элементы отсутствуют;
lastElementChild
— последний дочерний элемент. Равен null
, если дочерние элементы отсутствуют;
nextElemtSibliting
— ссылается на следующий (справа) соседний элемент. Равен null
, если следующий элементы отсутствуют;
previousElemtSibliting
— ссылается на предыдущий (слева) соседний элемент. Равен null
, если предыдущий элементы отсутствуют.
Все перечисленные тут свойства будут ссылаться только на HTML
элементам и они игнорируют текстовые узлы и комментарии в коде:
const elem = document.querySelector('#elem_3'),
parent = elem.parentNode, //
children = elem.children, // HTMLCollection c двумя ссылками и
firsChild = elem.firstElementChild, //
lastChild = elem.lastElementChild, //
prevNeighbour = elem.previousElementSibling, //
nextNeighbour = elem.nextElementSibling //
А вот например свойство childNodes
, будет содержать всех потомков элемента, включая комментарии и текст. На практике можно редко встретить ситуацию когда требуется взаимодействовать с тактовыми узлами, ещё реже с комментариями, так что свойства учитывающие, в практическом коде штука весьма диковинная, но тем не менее знать о них нужно.
При работе со всеми свойствами перечисленными выше, центральной точкой отсчёта становится определённый элемент, относительно которого мы можем двигаться по документу в любом направлении.
Ещё есть одно информационное свойство childrenElementCount
, хранящее количество потомков.
const elem = document.querySelector('#elem_3'),
childrenCount = elem.childElementCount // 2
Выводы
Обратите внимание на разнообразие, возможных способов, через которые можно реализовать работу с HTML
элементами в JS, многие из которых дублируют друг, друга. Причём аналогичная ситуация будет всплывать из раза в раз и в других интерфейсах языка и является одной из причин хейта в его сторону. Подобная хаотичность вызвана необходимостью обратной совместимости с фичами из разных вех развития JavaScript и его не простой историей успеха.
Я же в данном материале постарался описать всё что есть, указав какие фичи можно использовать, а какие допустимы только под угрозой применения к вам насилия. Код в котором встречаются только 2 одобренных метода, будет легко читаться всеми гребцами на галере, а вот использование всего зоопарка принуждает других разработчиков (да и вас самого пару месяцев спустя), либо держать в голове кучу не особо нужных нюансов, либо идти в гугл или ChatGPT за разъяснениями, что порой раздражает. Плохой разработчик пишет код для себя, хороший для других, помните это :-)