spam poison

Введение в Объектную Модель Документа ч.2


Итак, ранее мы рассмотрели как можно получить доступ к любому атрибуту или свойствам стилей любого элемента на странице. Но как же динамически добавлять или удалять сами элементы и контент на странице? Или изменять контент?


Итак, ранее мы рассмотрели как можно получить доступ к любому атрибуту или свойствам стилей любого элемента на странице. Но как же динамически добавлять или удалять сами элементы и контент на странице? Или изменять контент?

Динамический контент

Изменить текстовое содержимое страницы довольно просто. Каждая непрерывная строка символов в теле HTML страницы представляется текстовым узлом. Свойство nodeValue содержит этот текст. Изменяя его значение можно соответственно изменять текст на странице.

Текстовые узлы (Text Nodes)

Следующий пример использует простейший тэг параграфа <p> и текст внутри него. Кликайте по ссылкам, чтобы изменить этот текст:

<p id="sample1">Вот текст, который будем изменять.</p>

<p>
<a href="" onclick="document.getElementById('sample1').firstChild.nodeValue = 'If something`s hard to do...'; return false;">меняем раз</a>

::
<a href="" onclick="document.getElementById('sample1').firstChild.nodeValue = '...then it`s not worth doing!'; return false;">меняем еще</a>
</p>

Ну и сразу важное замечание. У текстового узла отсутствует атрибут id, который может быть у узла элемента. Таким образом напрямую, используя метод document.getElementById() или document.getElementsByTagName(), к нему обращаться нельзя.

Зато получить доступ к текстовому узлу можно зная его родительский узел (parent node), в данном случае элемент параграфа с id="sample1". Этот узел элемента содержит один дочерний узел (child node), тот самый текстовый узел, который мы хотим изменить. Вот как это может быть изображено:

Простое дерево узлов

Итак, document.getElementById('sample1').firstChild.nodeValue используется для доступа к этому текстовому узлу и чтения или изменения его значения, т.е. текста внутри параграфа.

Важно помнить, что текстовый узел может содержать только текст. Даже простые тэги <b> и <i> внутри строки текста порождают дерево узлов элементов и текстовых узлов. Возьмем вышеописанный пример и, добавив тэги, сделаем слово "текст" полужирным.

<p id="sample2">Вот <b>текст</b>, который будем изменять.</p>

и таким образом элемент параграфа с id="sample2" теперь содержит три дочерних узла вместо одного. Это будет: текстовый узел "Вот", узел элемента для пары тэгов <b></b> и текстовый узел ", который будем изменять." Узел элемента b содержит один дочерний узел, это текстовый узел "текст". То, что получилось, можно изобразить так:

Дерево узлов изменилось

А теперь посмотрим, что получилось после изменения разметки, но если оставить скрипт из предыдущего примера без изменений.

Изменение значения firstChild элемента "p" теперь приведет к замене только части текста "Вот". А если попытаетесь добавить разметку в значение текстового узла, браузер обработает ее как обычный текст, вот пример:

<p id="sample4">Вот <b>текст</b>, который будем изменять.</p>


<p>
<a href="" onclick="document.getElementById('sample4').firstChild.nodeValue = '<b>If something`s</b> hard to do...'; return false;">меняем раз</a>
::

<a href="" onclick="document.getElementById('sample4').firstChild.nodeValue = '...then it`s <i>not worth</i> doing!'; return false;">меняем еще</a<
</p>

Вы избежите проблему, рассмотренную выше, поняв, что текстовые узлы это обычные строки символов, размещенные между любыми двумя 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 по прежнему указывает на этот узел.



Article information
Wroted: Tue, 05 Jul 2005 13:56:00 EEST
Autors: Евгений Клоков , Андрей Баранов
Added by: AlexParamonov at Sun, 15 Feb 2009 13:56:53 EET
Modified by: AlexParamonov at Sun, 15 Feb 2009 14:04:10 EET
Translation information
Added by: AlexParamonov at Sun, 15 Feb 2009 13:57:28 EET
Modified by: AlexParamonov at Sun, 15 Feb 2009 14:07:25 EET
Copyright © 2008-2010 Alexander Paramonov
Valid XHTML 1.0 Transitional