Выбрать главу

Поиск элементов

Часто бывает полезным ориентироваться по этим ссылкам между родителями, детьми и родственными узлами и проходить по всему документу. Однако если нам нужен конкретный узел в документе, очень неудобно идти по нему, начиная с 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.