Обход узлов документа может быть полезной функцией, но реальную мощь базо_ вой модели DOM API обеспечивают средства, позволяющие использовать Java_ Script для динамического изменения документов. Следующие примеры демон_ стрируют основные приемы модификации документов и некоторые другие воз_ можности.
340 Глава 15. Работа с документами
Пример 15.5 включает JavaScript_функцию sortkids(), примерный документ и HTML_кнопку, которая при щелчке на ней вызывает функцию sortkids() и пе_ редает ей идентификатор тега <ul>. Функция sotrkids() отыскивает элементы, до_ черние по отношению к заданному, выполняет сортировку, основываясь на тек_ стовом содержимом, и методом appendChild() переупорядочивает элементы в до_ кументе так, чтобы они шли друг за другом в алфавитном порядке.
Пример 15.5. Сортировка элементов в алфавитном порядке
<script>
function sortkids(e) {
// Это элемент, потомки которого должны быть отсортированы if (typeof e == "string") e = document.getElementById(e);
// Скопировать дочерние элементы (не текстовые узлы) в массив var kids = [];
for(var x = e.firstChild; x != null; x = x.nextSibling)
if (x.nodeType == 1 /* Node.ELEMENT_NODE */) kids.push(x);
// Выполнить сортировку массива на основе текстового содержимого
// каждого дочернего элемента. Здесь предполагается, что каждый
// потомок имеет единственный вложенный элемент – узел Text kids.sort(function(n, m) { // Функция сравнения для сортировки
var s = n.firstChild.data; // Текстовое содержимое узла n
var t =
m.firstChild.data; // Текстовое содержимое узла m
if (s <
t)
return _1;
// Узел n
должен
быть
выше узла m
else
if
(s
> t) return 1;
//
Узел
n
должен
быть
ниже узла m
else
return 0;
//
Узлы
n
и m эквивалентны
});
// Теперь нужно перенести узлы_потомки обратно в родительский элемент
// в отсортированном порядке. Когда в документ вставляется уже
// существующий элемент, он автоматически удаляется из текущей позиции,
// в результате операция добавления этих копий элементов автоматически
// перемещает их из старой позиции. Примечательно, что все текстовые узлы,
// которые были пропущены, останутся на месте.
for(var i = 0; i < kids.length; i++) e.appendChild(kids[i]);
}
</script>
<ul id="list"> <!—Этот список будет отсортирован __> <li>один<li>два<li>три<li>четыре<li>пять
<!__ элементы не в алфавитном порядке __> </ul>
<!__ кнопка, щелчок на которой запускает сортировку списка __> <button onclick="sortkids('list')">Сортировать</button>
Результаты выполнения примера 15.5 на рис. 15.3 демонстрируют, что после щелчка на кнопке элементы списка сортируются в алфавитном порядке.
Обратите внимание: в примере 15.5 узлы сначала копируются в отдельный мас_ сив. Это не только позволяет упростить сортировку, но имеет и другие преиму_ щества. Объекты NodeList, которые являются значениями свойства childNodes и возвращаются методом getElementByTagName(), являются «живыми», т. е. любые изменения в документе немедленно отражаются на объекте NodeList. Это может стать источником определенных сложностей, когда добавление или удаление уз_ лов списка производится в процессе обхода этого списка. По этой причине гораз_
15.7. Модификация документа
Рис. 15.3. Список до и после сортировки
до безопаснее сделать «мгновенный снимок» узлов, скопировав их в настоящий массив, прежде чем выполнять их обход.
Пример 15.5 изменяет структуру документа, переупорядочивая элементы. При_ мер 15.6 изменяет содержимое документа, изменяя его текст. В этом примере определяется функция upcase(), которая рекурсивно обходит все дочерние узлы заданного узла и преобразует символы во всех текстовых узлах, приводя их к верхнему регистру.
Пример 15.6. Преобразование содержимого документа в верхний регистр
// Эта функция рекурсивно обходит узел n и всех его потомков, заменяя
// все узлы Text их эквивалентами в верхнем регистре.
function upcase(n) {
if (n.nodeType == 3 /*Node.TEXT_NODE*/) {
// Если это узел Text, преобразовать его в верхний регистр. n.data = n.data.toUpperCase( );
}
else {
// Если это не узел Text, обойти его потомков
// и рекурсивно вызвать эту функцию для каждого потомка. var kids = n.childNodes;
for(var i = 0; i < kids.length; i++) upcase(kids[i]);
}
}
В примере 15.6 просто изменяется содержимое свойства data каждого встреченно_ го текстового узла. Кроме того, существует возможность добавлять, удалять и за_ менять текст внутри узла Text с помощью методов appendData(), insertData(), de_ leteData() и replaceData(). Эти методы не определены непосредственно в интер_ фейсе Text, а наследуются им от интерфейса CharacterData. Дополнительные сведе_ ния об этих методах можно найти в описании CharacterData в части IV книги.
В примере 15.5 выполнялось переупорядочивание элементов документа, но при этом их родительский элемент оставался тем же самым. Однако следует заме_ тить, что DOM API позволяет свободно перемещать узлы по дереву документа (но только в пределах одного документа). Пример 15.7 демонстрирует это путем
342 Глава 15. Работа с документами
определения функции с именем embolden(), заменяющей указанный узел новым элементом (созданным с помощью метода createElement() объекта Document), пред_ ставляющим HTML_тег <b>, и делающей исходный узел дочерним для нового уз_ ла <b>. В HTML_документе благодаря этому любой текст в данном узле или в его потомках отображается жирным шрифтом.
Пример 15.7. Замена родителя узла элементом <b>
<script>
// Эта функция принимает в качестве аргумента узел n, заменяет его
// в дереве узлом Element, представляющим HTML_тег <b>, а затем делает
// исходный узел дочерним для нового элемента <b>.
function embolden(n) {
if (typeof n == “string”) n = document.getElementById(n); // Ищем узел
var b = document.createElement("b"); // Создаем новый элемент <b>
var parent = n.parentNode; // Получаем родительский узел