Каждый отдельный узел, будь то html-элемент, его атрибут или текст, в структуре DOM представлен объектом Node. Может возникнуть вопрос: как связаны элементы веб-страницы и узлы веб-страницы? И тут надо отметить, что любой элемент веб-страницы является узлом, но не любой узел является элементом (например, атрибуты и текст элементов также являются отдельными узлами).
Объект Node предоставляет ряд свойств, с помощью которых мы можем получить информацию о данном узле:
childNodes: содержит коллекцию дочерних узлов
children: содержит коллекцию дочерних узлов, которые являются элементами
firstChild: возвращает первый дочерний узел текущего узла
firstElementChild: возвращает первый дочерний узел, который является элементом
lastChild: возвращает последний дочерний узел текущего узла
lastElementChild: возвращает последний дочерний узел, который является элементом
previousSibling: возвращает предыдущий узел, который находится на одном уровне с текущим
nextSibling: возвращает следующий узел, который находится на одном уровне с текущим
previousElementSibling: возвращает предыдущий узел, который является элементом и который находится на одном уровне с текущим
nextElementSibling: возвращает следующий узел, который является элементом и который находится на одном уровне с текущим
ownerDocument: возвращает корневой узел документа
parentNode: возвращает родительский узел для текущего узла
parentElement: возвращает родительский узел, который является элементом
nodeName: возвращает имя узла
nodeType: возвращает тип узла в виде числа
nodeValue: возвращает текст текстового узла
Прежде всего мы можем использовать свойства nodeName и nodeType, чтобы узнать тип узла:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="article"> <h1 id="header">Home Page</h1> <p>Page Text</p> </div> <script> const article = document.getElementById("article"); console.log(article.nodeName); // DIV console.log(article.nodeType); // 1 </script> </body> </html>
Здесь получаем информацию по элементу с id="header". В частности, свойство nodeName
возвратит имя тега элемента - div,
а свойство nodeType
число 1. Каждому типу узлов соответствует определенное число:
nodeType | Тип узла |
1 | элемент |
2 | атрибут |
3 | текст |
Для получения родительского элемента применяются свойства parentNode и parentElement. Например:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="article"> <h1 id="header">Home Page</h1> <p>Page Text</p> </div> <script> // выбираем все элемент c id="header" const header = document.getElementById("header"); // получаем родительский элемента const headerParent = header?.parentElement; // можно так // const headerParent = header?.parentNode; console.log(headerParent); // выводим родительский элемент на консоль </script> </body> </html>
Здесь выводим на консоль элемент, в который помещен элемент с id="header".
Стоит отметить, что хотя оба метода в принципе возвращают один и тот же элемент, однако есть исключение - элемент <html>
. Для него родительским
узлом будет объект document, а вот родительского элемента у него не будет (будет значение null):
const htmlEl = document.getElementsByTagName("html")[0]; const parentElem = htmlEl.parentElement; const parentNode = htmlEl.parentNode; console.log(parentElem); // null console.log(parentNode); // объект document
Метод hasChildNodes() возвращает true
, если элемент содержит вложенные узлы:
const article = document.querySelector("div"); if(article.hasChildNodes()){ console.log("There are child nodes"); } else{ console.log("No child nodes"); }
Для получения дочерних элементов можно использовать свойство children:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="article"> <h1 id="header">Home Page</h1> <p>Page Text</p> </div> <script> // выбираем элемент c id="article" const article = document.getElementById("article"); for(elem of article.children){ console.log(elem); } </script> </body> </html>
Здесь получаем элемент с id="article" и в цикле проходим по всем его дочерним элементам. А это два элемента:
<h1 id="header">Home Page</h1> <p>Page Text</p>
Если же нам надо выбрать вообще все дочерние узлы (не только элементы, но и атрибуты и текст), то применяется метод childNodes:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="article"> <h1 id="header">Home Page</h1> <p>Page Text</p> </div> <script> // выбираем элемент c id="article" const article = document.getElementById("article"); for(node of article.childNodes){ let type = ""; if(node.nodeType===1) type="элемент"; else if(node.nodeType===2) type="атрибут"; else if(node.nodeType===3) type="текст"; console.log(node.nodeName, ": ", type); } </script> </body> </html>
Здесь мы выбираем тот же элемент, но теперь перебираем его узлы. выбираем элемент div с классом article и пробегаемся по его дочерним узлам. И в цикле выводим имя узла и его тип с помощью свойств nodeName и nodeType.
И несмотря на то, что в блоке div#article только два элемента: заголовок h1 и параграф, консоль отобразит нам пять узлов.
#text : текст H1 : элемент #text : текст P : элемент #text : текст
Дело в том, что пробелы между узлами также считаются за отдельные текстовые узлы. Если бы пробелов не было:
<div id="article"><h1 id="header">Home Page</h1><p>Page Text</p></div>
то при переборе мы бы обнаружили только два дочерних узла, как и ожидалось.
Кроме того, для получения первого и последнего узла/элемента применяются свойства firstChild/firstElementChild и lastChild/lastElementChild соответственно.
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="article"> <h1 id="header">Home Page</h1> <p>Page Text</p> </div> <script> const article = document.getElementById("article"); console.log("First Child:", article.firstElementChild); console.log("Last Child:", article.lastElementChild); </script> </body> </html>
Консольный вывод:
First Child: <h1 id="header">Home Page</h1> Last Child: <p>Page Text</p>
Для получения количества дочерних элементов можно применять свойство childElementCount. Это значение будет эквивалентно значению
children.length
:
const article = document.getElementById("article"); console.log(article.childElementCount); // 2 console.log(article.children.length); // 2
Свойства previousSibling/previousElementSibling и nextSibling/nextElementSibling позволяют получить предыдущий и следующий элементы, которые располагаются на одном уровне с текущим. Например:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="article"> <h1 id="header">Home Page</h1> <p>Page Text 1</p> <p>Page Text 2</p> <p>Page Text 3</p> </div> <script> const article = document.getElementById("article"); let tempNode = article.firstElementChild; while(tempNode != null){ console.log(tempNode); tempNode = tempNode.nextElementSibling } </script> </body> </html>
Здесь опять же получаем элемент с id="article". Затем получаем его первый элемент в переменную tempNode и в цикле, пока tempNode не будет равен null, выводим его значение на консоль и потом присваиваем этой переменной следующий элемент того же уровня (соседний элемент)
tempNode = tempNode.nextElementSibling
Таким образом, мы перебирем все элементы одного уровня. Консольный вывод:
<h1 id="header">Home Page</h1> <p>Page Text 1</p> <p>Page Text 2</p> <p>Page Text 3</p>
Также можно перебрать узлы в обратном порядке: сначала получаем последний узел, а затем обращаемся к предыдущему сестринскому узлу:
const article = document.getElementById("article"); let tempNode = article.lastElementChild; while(tempNode != null){ console.log(tempNode); tempNode = tempNode.previousElementSibling; }
Свойство nodeValue позволяет получить содержимое текстового узла, то есть его текст. Например:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="article"> <h1 id="header">Home Page</h1> <p id="text">Page Text</p> </div> <script> // получаем элемент с id="text" const pageText = document.getElementById("text"); console.log(pageText.nodeValue); // null for(textNode of pageText.childNodes){ console.log(textNode.nodeValue); } </script> </body> </html>
В данном случае мы пытаемся получить текст элемента с id="text". Сначала получаем данный элемент в константу pageText. Однако напрямую мы не можем у него вызвать у него свойство nodeValue. Если мы это сделаем, то получим null:
console.log(pageText.nodeValue); // null
Потому что полученный нами элемент не является текстовым узлом. Текстовый узел располагается внутри элемента pageText. И чтобы получить текст, нам надо обратиться к этому текстовому узлу через коллекцию childNodes:
for(textNode of pageText.childNodes){ console.log(textNode.nodeValue); }
Хотя мы так можем получть текстовое содержимое элементов, но это не самый оптимальный способ, и далее мы рассмотрим другие способы.