Статьи

Перевод: Мациевский Николай aka sunnybear
Опубликована: 22 июля 2008

DOM DocumentFragment: быстрее быстрого

Примечание: ниже перевод заметки от John Resig DOM "DOM DocumentFragments", в которой автор рассказывает об опыте использования DocumentFragment и сравнивает его быстродействие с обычным appendChild. Мои комментарии далее курсивом.

Недавно я игрался с DOM DocumentFragment в JavaScript, пробуя, что же можно с ним сотворить. Если вкратце, то DocumentFragment является облегченным контейнером для DOM-узлов. Он является частью спецификации DOM 1 и поддерживается во всех современных браузерах (был добавлен в Internet Explorer в 6 версии).

В процессе чтения я наткнулся на интересную вещь. Цитирую из спецификации:

Также различные операции — например, добавление узлов как дочерних для другого Node — могут принимать в качестве аргумента объекты DocumentFragment; в результате этого все дочерние узлы данного DocumentFragment перемещаются в список дочерних узлов текущего узла.

Это означает, что если у вас есть группа DOM-узлов, которые вы добавляете к фрагменту документа, то после этого можно этот фрагмент просто добавить к самому документу (вы можете добиться того же самого результата, если добавите каждый узел к документу в индивидуальном порядке). Я тут же почувствовал возможный выигрыш в производительности. Я немного исследовал этот момент и обнаружил, что DocumentFragment также поддерживает метод cloneNode. Это обеспечивает нас полной функциональностью для экстремальной оптимизации процесса добавления узла в DOM-дерево.

Я создал простую демо-версию для проверки этой теории.

Давайте рассмотрим ситуацию, когда у вас есть группа узлов, которую нужно добавить к DOM-дереву документа (в демо-версии это 12 узлов — 8 на верхнем уровне — против целой кучи div).

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) );
}

При проведении замеров времени можно увидеть следующую картину:

БраузерНормальный (ms)Fragment (ms)
Firefox 3.0.19047
Safari 3.1.215644
Opera 9.5120895
IE 6401140
IE 723061
IE 8b112040

В качестве заключения: метод, который игнорируется в современной веб-разработке, может привести к значительному ускорению (в 2–3 раза) работы ваших преобразований DOM-дерева.

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

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

var div = document.getElementsByTagName("div");
    var dv = document.createElement("div");
    var parent = div[0].parentNode;

    for ( var e = 0; e < elems.length; e++ ) {
	dv.appendChild( elems[e].cloneNode(true) );
    }

    for ( var i = 0; i < div.length; i++ ) {
// for IE
	parent.replaceChild(dv.cloneNode(true),div[i]);
// for other
	div[i] = dv.cloneNode(true);
    }

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

Читать дальше

Все комментарии (habrahabr.ru)