13. Объектная модель документа. DOM W3C
Примеры 06–10
Пример 06. Обход дерева элементов
Постановка задачи
Написать скрипт, который в каждый элемент внутри BODY добавляет атрибут title со значением, равным имени тега этого элемента. Кроме того, скрипт должен подсчитывать число элементов внутри BODY.
Решение
Решение основано на рекурсивной функции, описанной ниже.
// Функция получает узел в качестве аргумента,
// рекурсивно вычисляет количество узлов типа Element (считая сам корень)
// и в каждый такой узел вставляет атрибут title с именем тега элемента.
// Функция возвращает количество узлов типа Element (считая корень).
function skipping(node)
{
var numElements = 0; // Счетчик узлов Element
// Если node есть узел Element, увеличиваем счетчик на 1
// и дополняем элемент атрибутом title с именем тега элемента.
if (node.nodeType == 1 /* Node.ELEMENT_NODE */)
{
numElements++;
node.title = node.tagName;
}
var children = node.childNodes; // Получим список дочерних элементов
for(var i=0; i<children.length; i++) // Смотрим все дочерние элементы
numElements += skipping(children[i]); // Рекурсия по дочерним элементам
return numElements; // Возвращаем число элементов
}
Смотрите: Пример 06
Пример 07. Внешние ссылки
Постановка задачи
Отыскать в документе все внешние ссылки и присвоить им класс (атрибут class) с именем off. В CSS-файле для класса off написать определение, которое снабжало бы внешние ссылки специальной графической пиктограммой.
Решение
Задача решается следующим кодом:
// Вызов setOffLinks для BODY
setOffLinks(document.body, "off");
// Функция обходит дерево узла node в поиске элементов
// <A href="http...">...</A> -- внешних ссылок, и в каждый
// такой элемент вставляет атрибут class=cl
function setOffLinks(node, cl)
{
// Если это элемент, и это элемент A, и значение его
// атрибута href начинаетя с "http" (внешняя ссылка),
// то присвоить атрибуту class значение cl.
if (node.nodeType == 1 &&
node.tagName.toLowerCase() == "a" &&
node.href.search(/^http/) != -1) node.className = cl;
// Рекурсивный обход дочерних узлов
var children = node.childNodes; // Получим список дочерних элементов
for(var i=0; i<children.length; i++) // Смотрим все дочерние элементы
setOffLinks(children[i],cl); // Рекурсия по дочерним элементам
}
Смотрите: Пример 07
Пример 08. Отбор элементов
Задачу, поставленную в примере 7 можно решить гораздо проще при помощи метода getElementsByTagName. С помощью этого метода можно получить массив со ссылками на нужные элементы, а потом присвоить свойству className для внешних сылок значение off:
var list = document.body.getElementsByTagName("a");
for(var i=0; i<list.length; i++)
if (list[i].href.search(/^http/) != -1) list[i].className = "off";
Разделение работы на две части — сначала отбор элементов, затем оперирование с элементами в полученном наборе, практикуется довольно часто. Для выполнения этого плана полезна функция, которая отбирает элементы по заданным признакам и помещает ссылки на них в массив (заметьте, не сами элементы, они остаются на своих местах в DOM, а только ссылки). Когда такой массив получен, дальнейшая работа выполняется с его элементами.
Таким образом, работу с документом можно выполнять в два этапа:
- Получение набора элементов.
- Выполнение действий с элементами полученного набора.
Такое разделение удобно, ибо первая часть работы инвариантна, и может быть оформлена в виде общей универсальной функции. Именно такой подход лежит в основе функционирования библиотеки jQuery, с которой мы познакомимся в заметке 15.
В этом примере рассмотрим универсальную функцию getElements для построения набора элементов по заданным признакам и применим её к решению задачи из примера 7.
Ниже приводится код функции getElements, позаимствованный из-за его красоты из неоднократно упоминаемой книги Девида Флэнагана.
/**
* getElements(classname, tagname, root):
* Если какой-то промежуточный аргумент отсутствует, используйте null.
* Функция возвращает массив ссылок на DOM-элементы, которые являются
* членами указанного класса, соответствуют тегам с заданным именем и
* вложены в элемент root.
*
* Если аргумент classname не определен, отбор элементов производится
* без учета принадлежности к какому-то определенному классу.
* Если аргумент tagname не определен, отбор элементов производится
* без учета имени тега.
* Если аргумент root не определён, поиск производится в объекте
* document.
* Если аргумент root является строкой, он воспринимается как
* идентификатор элемента и поиск производится среди элементов,
* вложенных в элемент с этим идентификатором.
*/
function getElements(classname, tagname, root)
{
// Если root не задан, произвести поиск по всему документу
// Если это строка, найти сам объект
if (!root) root = document;
else if(typeof root=="string") root=document.getElementById(root);
// Если имя тега не определено, искать без учета имени тега
if (!tagname) tagname = "*";
// Искать элементы, вложенные в элемент root и имеющие
// заданное имя тега
var all = root.getElementsByTagName(tagname);
// Если имя класса не определено,
// вернуть все теги без учета имени класса
if (!classname) return all;
// В противном случае отобрать элементы по имени класса
var elements = []; // Создается пустой массив
for(var i = 0; i < all.length; i++)
{
var element = all[i];
if (isMember(element, classname)) // isMember() определена далее
elements.push(element); // Добавлять члены класса в массив
}
// Вернуть результат (всегда возвращается массив, даже если он пустой)
return elements;
// Функция определяет принадлежность элемента к заданному классу.
// Эта функция оптимизирована для случая, когда свойство
// classname содержит единственное имя класса, но учитывает
// возможность наличия нескольких имен классов, разделенных
// пробелами (это допускается синтаксисом языка CSS).
function isMember(element, classname)
{
var classes = element.className; // Получить список классов
if (!classes) return false; // Класс не определен
if (classes == classname) return true; // Точное совпадение
// Нет точного совпадения, поэтому если в списке нет пробелов,
// то этот элемент не является членом класса.
// Для проверки используется метод test объекта
// RegExp (регулярные выражения)
var whitespace = /\s+/;
if (!whitespace.test(classes)) return false;
// В этой точке известно, что элемент принадлежит нескольким
// классам, поэтому нужно проверить каждый из них
var c = classes.split(whitespace); // Разбить по символам пробела
for(var i = 0; i < c.length; i++) // Цикл по всем классам
if (c[i] == classname) return true; // Проверить совпадение
return false; // Не обнаружено ни одного совпадения
}
}
Смотрите: Пример 08
Пример 09. Галерея изображений
Постановка задачи
Создать приложение для демонстрации картинок. Картинки в уменьшенном размере (в виде иконок) образуют меню выбора. Щелчок на иконке выводит картинку в натуральных размерах в область показа.
Решение
Смотрите: Пример 09
Пример 10. Перестановка элементов в обратном порядке
Постановка задачи
Написать функцию, которая меняет следование дочерних узлов элемента на обратный. Продемонстрировать работу функции на примере со списком UL.
Решение
Функция описана на страницах книги Девида Флэнагана.
// Меняет порядок следования дочерних узлов узла n на обратный
function reverse(n)
{
// Создать пустой объект DocumentFragment (временное хранилище узлов)
var f = document.createDocumentFragment();
// Обойти все дочерние узлы в обратном порядке и переместить
// их во временное хранилище. Последний дочерний элемент узла n
// станет первым дочерним узлом f, и наоборот. Обратите внимание:
// добавление узла в f автоматически вызовет его удаление из n.
while(n.lastChild) f.appendChild(n.lastChild);
// Переместим все дочерние узлы из f обратно в n за один шаг
// (при этом f станет пустым).
n.appendChild(f);
}
Смотрите: Пример 10