Начало · Справочники · Курсы · Разговоры

leechy.ru · Сайт почти придуман

DOM: Коротко о Создании

Уже есть представление о том, каким образом можно и нужно работать с DOM-документами, а также каким образом можем их загружать в документ в котором находимся. Осталось рассказать каким образом с помощью DOM-методов можно создавать вставлять в текущую страницу совершенно новых элементов.

Создание элементов

Под словом «элемент» подразумевается «тэг», т.е. <p>, <a>, <table> и даже <br> и <hr> — это элементы. Любой текст — это не элемент, о нем чуть ниже.

Основной DOM-метод, который отвечает за создание элементов — это createElement(). Это метод документа. Простыми словами — его нужно вызывать как document.createElement(). Более сложными — создаваемый элемент должен отвечать языку документа, в котором мы хотим вставить элемент. Поэтому создавая «нестандартные» элементы с помощью метода createElement() они даже в MSIE5 попадают в дерево документа и с ними можно работать так, как я описывал в статье «DOM: Нестандартный HTML». Но сейчас речь о другом.

Итак, единственный параметр, который нужно передать методу — это строка, в которой указывается какой элемент именно хотите создать. Например, если хотите создать параграф, то нужно написать:

var newParagraph = document.createElement('p');

При этом новый элемент создается в памяти браузера и все еще нигде не показывается. Чтобы не «потерять» его, метод возвращает указатель на этот элемент, который можно записать в переменную (в данном случае — newParagraph).

Метод createElement() работает отлично в NN6/Mozilla и MSIE5+, но в Internet Explorer можно еще писать не только имя тэга, но и весь тэг целиком вместе с атрибутами и пр.:

if (ie5) {
   var newParagraph = document.createElement('<p align="right">');
}

Знать это полезно, потому, что есть некоторые атрибуты, которые в MSIE нельзя установить с помощью метода setAttribute(). Подобные атрибуты, которые я встречал — это специфические атрибуты у таблиц, которых нельзя установить стилями (в MSIE) — cellspacing, colspan и rowspan, а также сами атрибуты отвечающие за стили — class и style (учтите, что их может быть больше). Именно в таких случаев можно прибегнуть к синтаксису от MS. Но все-таки не нужно забывать про совместимость. К счастью есть соответствующие свойства самих элементов, с помощью которых можно устанавливать значения этих атрибутов. Итак, нужно писать так:

newTable.cellPadding = 0;
newCell.colSpan = 3;
newCell.rowSpan = 2;
newCell.className = 'selector';
newCell.style.paddingLeft = 17;

вместо:

newTable.setAttribute('cellpadding', '0');
newCell.setAttribute('colspan', '3');
newCell.setAttribute('rowspan', '2');
newCell.setAttribute('class', 'selector');
newCell.setAttribute('style', 'padding-left: 17pt;');

потому, что второе не будет работать в MSIE.

Если встретите другие «странности» Эксплорера, которые по-вашему мнению должны быть отражены в этой статье — напишите в форуме (заранее спасибо).

Создание текста

Для создании текстовых нодов есть специальный метод — createTextNode(). Он тоже метод документа и также вызывается document.createTextNode(). У него также есть один параметр — строка, из которой собственно будет состоять textNode. И также возвращает указатель на созданный элемент. Но от createElement() и даже от свойства innerHTML отличается тем, что создает исключительно текстовые ноды. Т.е. даже если попытаетесь создать элемент, например:

var newText = document.createTextNode('<p>Параграф</p>');

то, в итоге получите нод, в котором записано следующее:

&lt;p&gt;Параграф&lt;/p&gt;

Недостатком реализаций DOM в браузерах можно назвать то, что до сих пор ни в MSIE, ни в Mozilla не реализован метод с помощью которого нужно создавать ентити — createEntityReference(). Для них лично я использую свойство innerHTML. То-же самое относиться и к CDATA — не реализован метод createCDATASection(), но для HTML это не критично.

«От этого DOM'а меня что-то не вставляет...»

Невозможно! DOM вставляет, да еще как. Аж двумя похожими, но различными методами. Эти методы — appendChild() и insertBefore(). Оба метода — методы элементов. Т.е. их нужно вызывать из конкретного элемента, в котором хотите вставить новый (или не очень) элемент.

У appendChild() есть один параметр — нод или указатель на него, которого хотите вставить:

var documentBody = document.getElementsByTagName('BODY')[0];
...
documentBody.appendChild(newParagraph);

В данном примере в тело (первое ;-)) страницы добавиться параграф, которого мы создавали выше. Он будет последним элементом (lastChild). appendChild() всегда вставляет элемент после всех других под-элементов данного элемента.

В отличии от него у insertBefore() есть два параметра — элемент, который мы хотим разместить и еще указатель на элемент, перед которого новый элемент окажется:

documentBody.insertBefore(newParagraph, documentBody.firstChild);

Здесь элемент newParagraph окажется самым первым элементом на странице, поскольку мы его помещаем перед первым под-элементом.

Клонирование

В случае, если попытаетесь вставить на новое место элемент, который уже находится в DOM-дереве, то этот элемент просто поменяет свое местоположение, т.е. перед тем, как он появиться туда, куда вы хотите его поставить он исчезнет со своего предыдущего местонахождения в дереве.

Бывает, однако случаев, когда необходимо «размножить» элемент. Например, чтобы показывать копию элемента рядом с курсором мыши во время перетаскивания (об этом напишу, наверное, отдельную статью). Для этого существует метод cloneNode().

Элементы, которые клонируете, не появляются в дереве, также как и создаваемые новые элементы. Т.е. если хотите показать клонированный элемент на странице, то нужно его поставить в желаемое место. У метода cloneNode() есть один параметр — булевое значение, который указывает копировать или нет детей клонироваемого элемента:

var newBody = documentBody.cloneNode(true);

После этой комманды переменная newBody будет указывать на элемент типа <body> в котором будет все содержимое страницы.

Удаление

Раз можно создавать элементы, значить можно и удалять их. Это логично. И это делается с помощью метода removeChild(). Это метод элемента. Его нужно вызывать у родителя того элемента, которого хотите удалить. В качестве единственного параметра нужно указать собственно удаляемый элемент:

documentBody.removeChild(documentBody.firstChild);

Эта команда удалить первый под-элемент тела страницы (только в случае, если documentBody указывает на тело страницы, о том как «заселектить» его см. выше). Элемент удаляется вместе со всеми своими под-элементами и под-нодами. Полностью.

В MSIE, кроме removeChild() есть метод, который позволяет удалять только элемент, не трогая его детей, а точнее поднимая их всех в дереве на уровень выше. Это метод removeNode(). Это тоже метод элемента, но вызывается самим удаляемым элементом. В качесве параметра нужно указать true или false, удалять или не удалять детей, соответственно. Советую пользоваться этим методом только если вы наверняка уверены, что на страницу будут ходит исключительно Internet Explorer'ом для Windows — даже в версии для Mac OS этот метод не поддерживается.

Некоторые советы

1. Вовсе не обязательно для каждого нового элемента создавать свою переменную. Если вам нужно всего лишь добавить текстовый нод в элемент, то можно обойтись одной строкой:

newElement.appendChild(document.createTextNode('Содержание текстового нода'));

2. Если знаете точного местонахождения искомого элемента не обязательно искать его методами getElementsByTagName() или getElementById(). Методы и свойства DOM'а позволяют вызывать друг из друга, вот например нормально работающая строка:

var myNodeLength = myDoc.documentElement.firstChild.lastChild.previousSibling.length;

3. Нужно помнить, что создание документов с очень разветвленными деревьями или болших таблиц «с нуля», с использованием только DOM-методов — не самая благодарная работа. Для этого можете использовать innerHTML или даже старый добрый document.write(). Я в форуме не так давно писал, что использование innerHTML — не самая лучшая идея, но код для создания большой таблицы, требующий описание каждой ячейки и ее содержимое вряд ли будет являться самым изящным решением.

Используйте активно DOM-методы там, где есть хорошо структурированные данные, которые вы загрузили в виде XML-файла. Это тот случай, где можно использовать циклы и сделать достаточно компактный код.

Частично использование DOM-методов оправдано в т.н. web-applications. Это может быть как добавление еще одного текстового поля формы, когда все остальные заполнены, так и простая работа на уровне настроек пользователя. В любом случае я надеюсь, что ваши идеи можно реализовать какими бы они небыли.

Обещанный пример

Примером к этой и к предыдущей статьях будет служить HTML-файл, который загружает XML со всеми свойствами CSS level 1 и 2 и строит таблицу на его основе.

Я постарался включить все, кроме удаления и клонирования нодов — там есть и создание таблицы, ячейки с colspan'ом, определение классов и стилей, а также много ячеек заполняются ентитями. Скрипт не идеален, но надеюсь на его основе сможете разобраться что и как работает.

пример: cssref.html
xml: toc.xml