Выбрать главу

var elems = [

document.createElement("hr"),

text( document.createElement("b"), "Links:" ),

document.createTextNode(" "),

text( document.createElement("a"), "Link A" ),

document.createTextNode(" | "),

text( document.createElement("a"), "Link B" ),

document.createTextNode(" | "),

text( document.createElement("a"), "Link C" )

];

function text(node, txt){

node.appendChild( document.createTextNode(txt) );

return node;

}

Нормальное добавление

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

var div = document.getElementsByTagName("div");

for ( var i = 0; i < div.length; i++ ) {

for ( var e = 0; e < elems.length; e++ ) {

div[i].appendChild( elems[e].cloneNode(true) );

}

}

Добавление при помощи DocumentFragment

Однако если мы будем использовать DocumentFragment для совершения тех же самых операций, то ситуация изменится. Для начала мы добавим все наши узлы к самому фрагменту (используя имеющийся метод createDocumentFragment).

Самое интересное начинается тогда, когда мы собираемся добавить сами узлы в документ: нам нужно вызвать по одному разу appendChild и cloneNode для всех узлов!

var div = document.getElementsByTagName("div");

var fragment = document.createDocumentFragment();

for ( var e = 0; e < elems.length; e++ ) {

fragment.appendChild( elems[e] );

}

for ( var i = 0; i < div.length; i++ ) {

div[i].appendChild( fragment.cloneNode(true) );

}

При проведении замеров времени можно увидеть следующую картину (табл. 7.2).

Браузер

Нормальный

Fragment

Firefox 3.0.1

90

47

Safari 3.1.2

156

44

Opera 9.51

208

95

IE 6

401

140

IE 7

230

61

IE 8b1

120

40

Таблица 7.2. Сравнение методов работы с DOM-деревом, результаты в миллисекундах

А если еще быстрее?

Давайте подумаем еще немного. Зачем нам каждый раз создавать фрагмент документа, если мы для этой цели можем использовать обычный его узел (фактически создавать кэш нашего узла, который мы собираемся везде менять)? Так можно прийти к следующему фрагменту кода:

var div = document.getElementsByTagName("div");

var child = document.createElement("div");

var parent = div[0].parentNode;

for ( var e = 0; e < elems.length; e++ ) {

child.appendChild( elems[e].cloneNode(true) );

}

for ( var i = 0; i < div.length; i++ ) {

// для IE

if (IE) {

parent.replaceChild(child.cloneNode(true),div[i]);

// для других браузеров

} else {

div[i] = child.cloneNode(true);

}

}

В нем соответствующие узлы документа заменяются на клонированный вариант кэшированной версии (без создания DocumentFragemnt). Это работает еще быстрее (везде, кроме IE — примерно на порядок, в IE — в полтора-два раза).

innerHTML нам поможет

Чтобы уменьшить отрисовку отдельных частей документа в процессе добавления какого-либо большого фрагмента, мы можем сохранять HTML-код в виде текста и лишь на финальном этапе вставлять его в DOM-дерево. Давайте рассмотрим следующий пример:

var i, j, el, table, tbody, row, cell;

el = document.createElement("div");

document.body.appendChild(el);

table = document.createElement("table");

el.appendChild(table);

tbody = document.createElement("tbody");

table.appendChild(tbody);

for (i = 0; i < 1000; i++) {

row = document.createElement("tr");

for (j = 0; j < 5; j++) {

cell = document.createElement("td");

row.appendChild(cell);

}

tbody.appendChild(row);

}

Его можно значительно ускорить, если добавлять узлы не последовательно один за другим, а сначала создав HTML-строку со всем необходимым кодом, которая будет вставлена через innerHTML в конце всех операций.
В данном примере кроме уже указанного ускорения еще используется первоначальное создание массива элементов, которые можно объединить через свойство join в строку. Для больших строк это работает быстрее, чем последовательная конкатенация отдельных частей.

var i, j, el, idx, html;

idx = 0;

html = [];

html[idx++] = "<table>";

for (i = 0; i < 1000; i++) {

html[idx++] = "<tr>";

for (j = 0; j < 5; j++) {

html[idx++] = "<td></td>";

}

html[idx++] = "</tr>";

}

html[idx++] = "</table>";

el = document.createElement("div");

document.body.appendChild(el);

el.innerHTML = html.join("");

7.7. Кэширование в JavaScript

Очень часто в JavaScript используют глобальные объекты и переменные для чтения каких-либо параметров (или вызова определенных методов). Почти всегда этого можно избежать, если кэшировать объект из глобальной области видимости в локальную — все обращения к закэшированному объекту буду выполняться намного быстрее.

Итерации и локальное кэширование

При DOM-операциях перебор массива объектов является довольно типичной задачей. Давайте предположим, что вы разрабатываете HTML-приложение, которое индексирует содержание страниц. Нашей задачей является сбор всех элементов h1 на текущей странице, чтобы затем использовать их в проиндексированном массиве.