Недавно я игрался с 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.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 |
В качестве заключения: метод, который игнорируется в современной веб-разработке, может привести к значительному ускорению (в 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 — в полтора–два раза).