Devo usar document.createDocumentFragment ou document.createElement

97

Eu estava lendo sobre fragmentos de documentos e refluxo de DOM e me perguntei como document.createDocumentFragmentdiferem, document.createElementjá que parece que nenhum deles existe no DOM até que eu os acrescente a um elemento DOM.

Fiz um teste (abaixo) e todos demoraram exatamente a mesma quantidade de tempo (cerca de 95ms). Acho que isso pode ser devido ao fato de não haver nenhum estilo aplicado a qualquer um dos elementos, portanto, talvez não haja refluxo.

Enfim, com base no exemplo abaixo, por que devo usar ao createDocumentFragmentinvés de createElementao inserir no DOM e qual é a diferença entre os dois.

var htmz = "<ul>";
for (var i = 0; i < 2001; i++) {
    htmz += '<li><a href="#">link ' + i + '</a></li>';
}
htmz += '<ul>';

//createDocumentFragment
console.time('first');
var div = document.createElement("div");
div.innerHTML = htmz;
var fragment = document.createDocumentFragment();
while (div.firstChild) {
    fragment.appendChild(div.firstChild);
}
$('#first').append(fragment);
console.timeEnd('first');

//createElement
console.time('second');
var span = document.createElement("span");
span.innerHTML = htmz;
$('#second').append(span);
console.timeEnd('second');


//jQuery
console.time('third');
$('#third').append(htmz);
console.timeEnd('third');
screenm0nkey
fonte

Respostas:

98

A diferença é que um fragmento de documento efetivamente desaparece quando você o adiciona ao DOM. O que acontece é que todos os nós filhos do fragmento do documento são inseridos no local no DOM onde você insere o fragmento do documento e o próprio fragmento do documento não é inserido. O próprio fragmento continua a existir, mas agora não tem filhos.

Isso permite que você insira vários nós no DOM ao mesmo tempo:

var frag = document.createDocumentFragment();
var textNode = frag.appendChild(document.createTextNode("Some text"));
var br = frag.appendChild(document.createElement("br"));
var body = document.body;
body.appendChild(frag);
alert(body.lastChild.tagName); // "BR"
alert(body.lastChild.previousSibling.data); // "Some text"
alert(frag.hasChildNodes()); // false
Tim Down
fonte
3
Obrigado pela resposta. Você diz que permite várias inserções ao mesmo tempo, mas posso conseguir isso usando doc.createElement. A única diferença é que eu tinha que envolver os elementos em uma tag <span> primeiro e, em seguida, inserir esse <span> no DOM. É por isso que devo usar createDocumentFragment? Para evitar que elementos desnecessários sejam inseridos no DOM?
screenm0nkey
4
Sim, esse é definitivamente um dos motivos para usá-lo. Além disso, um fragmento de documento pode conter qualquer tipo de nó, enquanto um elemento não pode.
Tim Down
3
Na verdade, o fragmento não "desaparece"; o navegador move seus childNodes para o DOM. Portanto, o fragmento ainda existirá, mas estará vazio após a inserção.
inf3rno
1
@ inf3rno: Daí o meu uso da palavra "efetivamente" e a explicação a seguir, que é muito parecida com a sua. Admito que deveria ter dito explicitamente na resposta que o fragmento continua a existir sem nós filhos.
Tim Down
1
@TimDown obrigado pela sua resposta! Se entendi direito, em relação ao desempenho, o uso createFragmente createElementna memória tem quase o mesmo efeito, pois ambos atualizaram o DOM em lote em vez de atualizar iterativamente várias vezes. Embora o principal benefício do createFragmentseja oferecer a flexibilidade de escolher quaisquer elementos filho para anexar gratuitamente? Corrija-me se eu estiver errado.
Xlee de
9

Outra diferença muito importante entre criar um elemento e um fragmento de documento:

Quando você cria um elemento e o anexa ao DOM, o elemento é anexado ao DOM, assim como aos filhos.

Com um fragmento de documento, apenas os filhos são anexados.

Veja o caso de:

var ul = document.getElementById("ul_test");


// First. add a document fragment:


(function() {
  var frag = document.createDocumentFragment();
  
  
  var li = document.createElement("li");
  li.appendChild(document.createTextNode("Document Fragment"));
  frag.appendChild(li);
  
  ul.appendChild(frag);
  console.log(2);
}());

(function() {
  var div = document.createElement("div");
  
  
  var li = document.createElement("li");
  li.appendChild(document.createTextNode("Inside Div"));
   div.appendChild(li);
  
  ul.appendChild(div);
}());
Sample List:
<ul id="ul_test"></ul>

que resulta neste HTML malformado (espaço em branco adicionado)

<ul id="ul_test">
  <li>Document Fragment</li>
  <div><li>Inside Div</li></div>
</ul>
Jeremy J Starcher
fonte