Итак, ранее мы рассмотрели как можно получить доступ к любому атрибуту или свойствам стилей любого элемента на странице. Но как же динамически добавлять или удалять сами элементы и контент на странице? Или изменять контент?
Изменить текстовое содержимое страницы довольно просто. Каждая непрерывная строка символов в теле HTML страницы представляется текстовым узлом. Свойство nodeValue содержит этот текст. Изменяя его значение можно соответственно изменять текст на странице.
Следующий пример использует простейший тэг параграфа <p> и текст внутри него. Кликайте по ссылкам, чтобы изменить этот текст:
Ну и сразу важное замечание. У текстового узла отсутствует атрибут id, который может быть у узла элемента. Таким образом напрямую, используя метод document.getElementById() или document.getElementsByTagName(), к нему обращаться нельзя.
Зато получить доступ к текстовому узлу можно зная его родительский узел (parent node), в данном случае элемент параграфа с id="sample1". Этот узел элемента содержит один дочерний узел (child node), тот самый текстовый узел, который мы хотим изменить. Вот как это может быть изображено:
Важно помнить, что текстовый узел может содержать только текст. Даже простые тэги <b> и <i> внутри строки текста порождают дерево узлов элементов и текстовых узлов. Возьмем вышеописанный пример и, добавив тэги, сделаем слово "текст" полужирным.
А теперь посмотрим, что получилось после изменения разметки, но если оставить скрипт из предыдущего примера без изменений.
Вы избежите проблему, рассмотренную выше, поняв, что текстовые узлы это обычные строки символов, размещенные между любыми двумя HTML тэгами, причем не обязательно парными.
Свойство innerHTML
В Internet Explorer у узла элемента (element node) есть свойство innerHTML. Оно представляет собой символьные данные, между начальным и конечным тэгами и может включать другие HTML тэги. Netscape 6 тоже поддерживает это свойство, несмотря на то, что оно не входит в существующий стандарт DOM.
Используя это свойство, можно заменять все содержимое элемента параграфа из приведенного выше примера, включая HTML разметку, т.е. примерно так:
document.getElementById('sample4').innerHtml = "<b>If something`s</b> hard to do...";
Пока это свойство поддерживается браузерами, его можно использовать, но следует помнить, что оно не включено в стандарт DOM.
Добавление узлов
Добавление новых узлов также не представляет особых трудностей. Ранее мы рассмотрели, как изменять узлы атрибутов и применять их к элементам, а также, как изменять текстовые узлы в дереве документа (свойство innerHTML в расчет не берем).
Первый шаг к созданию узла объекта того типа, который вам нужен — это один из вариантов: document.createElement(), document.createAttribute() или document.createTextNode(). Однако для атрибутов вы можете захотеть создать узел элемента и сразу определить ему атрибут (помните, что IE стал поддерживать createAttribute() только начиная с 6 версии).
Использование текстовых узлов
Начнем с текстового узла. Этот пример кода показывает как создать текстовый узел и присвоить ему какое-то значение:
var myTextNode = document.createTextNode("ваш текст");
Теперь у вас есть текстовый узел. Но он не является частью дерева документа. Чтобы он появился на странице, его нужно сделать дочерним (child) по отношению к какому-нибудь уже существующему внутри дерева узлу. Его нельзя присоединить к другому текстовому узлу, так как текстовые узлы не могут содержать дочерние узлы. Узлы атрибутов не являются частью дерева документа, поэтому и к ним присоединить текстовый узел нельзя. Тогда в нашем распоряжении остаются узлы элементов.
Узел элемента может содержать несколько дочерних узлов, поэтому есть несколько методов, определяющих как и куда добавить новый узел к уже существующим дочерним узлам. Это лучше показать на примерах.
Метод appendChild() можно использовать, чтобы добавить новый текст (текстовый узел) в узел параграфа. При этом удалить последний из добавленных узлов можно с помощью метода removeChild().
<p id="sample1">Просто текст.</p>
... вызов функции добавления узла ...
var text = document.createTextNode(" новый текст " + (++counter1));
var el = document.getElementById("sample1");
el.appendChild(text);
... вызов функции удаления последнего (lastChild) узла ...
var el = document.getElementById("sample1");
if (el.hasChildNodes())
el.removeChild(el.lastChild);
Добавление текста — это просто: надо создать новый текстовый узел, определить узел элемента параграфа и вызвать метод appendChild(), чтобы добавить текстовый узел в конец его массива дочерних узлов (childNodes). Глобальную переменную counter добавляем в текст чтобы отличить каждый новый текстовый узел в окне браузера.
Удаление текста происходит также просто: removeChildNode(). Только в отличие от добавления обязательно нужно указывать какой из дочерних узлов должен быть удален. Здесь мы используем свойство элемента lastChild, которое всегда указывает на последний узел в массиве дочерних узлов элемента (childNodes). Таким способом можно удалить даже текст, который мог уже существовать в HTML-коде внутри тэга <p> еще до добавления новых текстовых узлов.
Стоит обрать внимание на метод hasChildNodes(), который просто возвращает true или false, показывая, содержит ли данный узел какие-нибудь дочерние узлы. Его следует использовать, чтобы избежать ошибок при вызове removeChild когда у узла может не быть дочерних узлов.
Соединение и разделение текстовых узлов
В вышеприведенном примере текстовые узлы добавлялись как самостоятельные дочерние узлы. Но если вписать текст в HTML-код,
<p id="sample1">Просто текст. новый текст 1 новый текст 2 новый текст 3 </p>
в DOM это будет одним дочерним текстовым узлом элемента параграфа. Другими словами, дерево узлов, собираемое динамическим добавлением контента, будет отличаться от дерева узлов, собранного из статического HTML-кода.
Оно не всегда может быть тем, что вы хотели получить. Иногда вам может понадобиться комбинировать текстовые узлы, таким образом, чтобы новый, динамически добавляемый контент включался в существующий статический HTML-код.
Такую возможность предоставляет метод normalize(). Вызов этого метода "подчищает" дерево узлов, собирая соседние текстовые узлы в единый текстовый узел и удаляя пустые.
Совместимость браузеров Пример нормализации параграфа, который разберем далее, не будет работать в Internet Explorer 5.5 и более ранних версиях, т.к. этот браузер не поддерживает метод normalize(). Поддержка это метода включена только начиная с 6 версии IE.
Этот пример похож на разобранный ранее, в нем мы сможем увидеть количество дочерних узлов параграфа до и после нормализации.
Здесь вы можете видеть, как после добавления текста увеличивается количество текстовых узлов, но после нормализации все добавленные узлы объединяются в единый текстовый узел.
... элемент для нормализации ...
el = document.getElementById("sample2");
el.normalize();
... показываем количество дочерних элементов ...
el = document.getElementById("sample2");
alert(el.childNodes.length);
Можно разделить текстовый узел на два отдельных, используя метод splitText(). Он пригодится, если вы захотите динамически изменить слово или фразу или вставить какой-нибудь элемент в текстовую строку.
Рассмотрим следующий пример, где кликнув по ссылке удалим первый дочерний элемент параграфа. Сперва возьмем целое предложение. Кликнув по ссылке "отделить слово", отделим первое слово в тексте, а кликнув после по ссылке "удалить первый узел", уберем только это слово из предложения. Кликнув по ссылке "сбросить" возвращаемся в исходное состояние.
... отделяем первое слово ...
el = document.getElementById("sample3");
if (el.hasChildNodes()) {
text = el.firstChild;
i = text.nodeValue.indexOf(" ");
if (i >= 0)
text.splitText(i + 1);
}
... удаляем первый текстовый узел ...
el = document.getElementById("sample3");
if (el.hasChildNodes())
el.removeChild(el.firstChild);
... возвращаем пример в исходное состояние ...
el = document.getElementById("sample3");
while (el.hasChildNodes())
el.removeChild(el.firstChild);
text = document.createTextNode(
"Какой-то текст внутри параграфа.");
el.appendChild(text);
Теперь пара замечаний: во-первых, для текстового узла nodeValue это просто строка. Метод splitText() полагается на начальное значение, которое покажет откуда можно начинать разбиение. Метод indexOf() используется для поиска в строке первого от ее начала пробела, чтобы передать его методу splitText().
Во-вторых, в коде для возврата в исходное состояние просто удаляются все дочерние узлы элемента параграфа, а затем добавляется исходный текст как единый текстовый узел.
Работа с узлами элементов (Element Nodes)
Добавление и удаление узлов элементов в основном похоже на действия с текстовыми узлами. Различие по-существу только в создании узла и размещении внутри него контента.
Начнем. Вы создаете узел, используя метод document.createElement(), которому передаете название тэга, который хотите создать, например "p", "div", "table" и т.п. Этот параметр регистронезависимый, т.е. "div", "Div" и "DIV" будут означать один и тот же тэг. Только помните, браузер вернет имя тэга в верхнем регистре, если попытаетесь у него это спросить, попробуйте:
var el = document.createElement("div");
alert(el.tagName);
в результате увидите "DIV".
Как и с текстовыми узлами, после создания узла элемента его надо добавить какому-нибудь существующему узлу элемента в дереве документа, чтобы затем его можно было увидеть на странице.
Но вновь созданный элемент — это просто пустой тэг. Поэтому ему неплохо добавить какие-нибудь атрибуты и текст. Следующий пример поможет это показать.
Здесь при клике по ссылке создаем новый элемент P со своими дочерними узлами, который присоединяем к существующему элементу DIV.
<div id="sample4">Этот текст внутри элемента DIV.</div>
... код ссылки ...
var paraEl, boldEl;
paraEl = document.createElement("p");
boldEl = document.createElement("b");
paraEl.appendChild(
document.createTextNode("Этот новый параграф с "));
boldEl.appendChild(document.createTextNode("полужирным"));
paraEl.appendChild(boldEl);
paraEl.appendChild(document.createTextNode(" текстом, добавлен в DIV"));
document.getElementById("sample4").appendChild(paraEl);
Сперва здесь создаем два новых элемента: тэги <p> и <b>. Затем элементу параграфа (P) добавляем новый текстовый узел со строкой "Этот новый параграф с ". Таким же образом добавляем "полужирным" элементу B. Затем добавляем этот элемент B (включая его дочерний текстовый узел) к параграфу. И следующий текстовый узел со строкой " текстом, добавлен в DIV" также добавляем к параграфу.
В данном случае элемент параграфа содержит три дочерних узла: текстовый узел (text node); узел элемента для тэга <b>; еще один текстовый узел. У элемента B только один текстовый узел. Завершающий этап — добавление вновь созданного параграфа существующему на странице тэгу <div>.
Можно установить какой-нибудь атрибут любому из этих новых элементов. Не имеет значения когда его устанавливать, до или после добавления элемента в дерево документа. Например,
boldEl.style.color = "#ffff00";
paraEl.appendChild(boldEl);
и
paraEl.appendChild(boldEl);
boldEl.style.color = "#ffff00";
сделают полужирный текст красным. Другими словами, appendChild() фактически помещает не узел в дерево, а только его копию. Переменная boldEl по прежнему указывает на этот узел.