Поиск элементов
Часто бывает полезным ориентироваться по этим ссылкам между родителями, детьми и родственными узлами и проходить по всему документу. Однако если нам нужен конкретный узел в документе, очень неудобно идти по нему, начиная с document.body
и тупо перебирая жёстко заданный в коде путь. Поступая так, мы вносим в программу допущения о точной структуре документа – а её мы позже можем захотеть поменять. Другой усложняющий фактор – текстовые узлы создаются даже для пробелов между узлами. В документе из примера у тега body
не три дочерних (h1
и два p
), а целых семь: эти три плюс пробелы до, после и между ними.
Так что если нам нужен атрибут href
из ссылки, мы не должны писать в программе что-то вроде: «второй ребёнок шестого ребёнка document.body». Лучше бы, если б мы могли сказать: «первая ссылка в документе». И так можно сделать:
var link = document.body.getElementsByTagName("a")[0];
console.log(link.href);
У всех узлов-элементов есть метод getElementsByTagName
, собирающий все элементы с данным тегом, которые происходят (прямые или не прямые потомки) от этого узла, и возвращает его в виде массивоподобного объекта.
Чтобы найти конкретный узел, можно задать ему атрибут id
и использовать метод document.getElementById
.
<p>Мой страус Гертруда:</p>
<p><img id="gertrude" src="img/ostrich.png"></p>
<script>
var ostrich = document.getElementById("gertrude");
console.log(ostrich.src);
</script>
Третий метод – getElementsByClassName
, который, как и getElementsByTagName
, ищет в содержимом узла-элемента и возвращает все элементы, содержащие в своём классе заданную строчку.
Меняем документ
Почти всё в структуре DOM можно менять. У узлов-элементов есть набор методов, которые используются для их изменения. Метод removeChild
удаляет заданную дочерний узел. Для добавления узла можно использовать appendChild
, который добавляет узел в конец списка, либо insertBefore
, добавляющий узел, переданную первым аргументом, перед узлом, переданным вторым аргументом.
<p>Один</p>
<p>Два</p>
<p>Три</p>
<script>
var paragraphs = document.body.getElementsByTagName("p");
document.body.insertBefore(paragraphs[2], paragraphs[0]);
</script>
Узел может существовать в документе только в одном месте. Поэтому вставляя параграф «Три» перед параграфом «Один» мы фактически удаляем его из конца списка и вставляем в начало, и получаем «Три/Один/Два». Все операции по вставке узла приведут к его исчезновению с текущей позиции (если у него таковая была).
Метод replaceChild
используется для замены одного дочернего узла другим. Он принимает два узла: новый, и тот, который надо заменить. Заменяемый узел должен быть дочерним узлом того элемента, чей метод мы вызываем. Как replaceChild
, так и insertBefore
в качестве первого аргумента ожидают получить новый узел.
Создание узлов
В следующем примере нам надо сделать скрипт, заменяющий все картинки (тег <img>
) в документе текстом, содержащимся в их атрибуте alt
, который задаёт альтернативное текстовое представление картинки.
Для этого надо не только удалить картинки, но и добавить новые текстовые узлы им на замену. Для этого мы используем метод document.createTextNode
.
<p>Это <img src="img/cat.png" alt="Кошка"> в
<img src="img/hat.png" alt="сапожках">.</p>
<p><button onclick="replaceImages()">Заменить</button></p>
<script>
function replaceImages() {
var images = document.body.getElementsByTagName("img");
for (var i = images.length - 1; i >= 0; i--) {
var image = images[i];
if (image.alt) {
var text = document.createTextNode(image.alt);
image.parentNode.replaceChild(text, image);
}
}
}
</script>
Получая строку, createTextNode
даёт нам тип 3 узла DOM (текстовый), который мы можем вставить в документ, чтобы он был показан на экране.
Цикл по картинкам начинается в конце списка узлов. Это сделано потому, что список узлов, возвращаемый методом getElementsByTagName
(или свойством childNodes
) постоянно обновляется при изменениях документа. Если б мы начали с начала, удаление первой картинки привело бы к потере списком первого элемента, и во время второго прохода цикла, когда i
равно 1, он бы остановился, потому что длина списка стала бы также равняться 1.
Если вам нужно работать с фиксированным списком узлов вместо «живого», можно преобразовать его в настоящий массив при помощи метода slice
.