Каждый отдельный узел, будь то 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);
}
Хотя мы так можем получть текстовое содержимое элементов, но это не самый оптимальный способ, и далее мы рассмотрим другие способы.