Imaginemos que temos que inserir vários elementos na tela (por exemplo, vários li dentro de uma ul), e que vamos fazer isso inserindo um por um (por exemplo, estamos fazendo isso cada vez que o callback de um forEach é executado), neste caso não é uma boa ideia usar o método "append", já que a cada li inserida, o navegador precisa fazer "reflow" e "repaint" da tela (a tradução de "reflow" e "repaint" é: "refluxo" e "redisenho"). Isto quer dizer que cada vez que uma li seja inserida na tela (ou seja, no DOM), o navegador precisa redesenhar os elementos e recalcular o fluxo dos elementos.
Mas acontece que "manipulação do DOM" é um processo lento e custoso para o processador do usuário, já que quanto mais manipulação do DOM a aplicação executa (estamos imaginando que centos ou Miles de li que tenham que ser inseridas em uma lista), mais o navegador vai exigir do processador por causa desse recalculo de fluxo e a exibição dos elementos. E como sabemos, não é uma boa ideia exigir muito o procesador do usuário.
Para resolver o problema anterior existe uma solução mais performática, e nesta abordagem o DOM é manipulado uma única vez (mesmo que agente tenha que inserir miles de li dentro de uma lista). Então, quando usamos esta solução mais performática, ao invés de inserir as li diretamente no DOM, elas vão ser inseridas em um fragmento de documento (document fragment).
Mas o que é o "document fragment"? Ele é um nó do DOM, ou seja, um elemento do DOM, e dentro dele agente pode colocar elementos que posteriormente agente vai colocar no DOM. Mas acontece que o "document fragment" só existe em memória, portanto, ele não é inserido na tela, e a grande sacada dele ser um nó do DOM que só existe em memória, é que agente pode adicionar outros nodes do DOM como filhos do "document fragment", e essa inserção de nodes no "document fragment" não vai fazer o navegador precisar redesenhar os elementos e recalcular o fluxo dos elementos no DOM, porque como já dito, o document fragment" não "vive" no DOM, ele "vive" na memória. Então, depois que os filhos do "documentFragment" são inseridos no DOM, o "documentFragment" continua estando na memória, mas vazio, porque seus filhos forma inseridos no DOM.
Para entender melhor vamos ver o seguinte exemplo:
const phrasesList = document.querySelector('[data-js="phrases-list"]'); //lista ul onde vamos adicionar as lis
const documentFragment = document.createDocumentFragment(); //aqui estamos criando o documet fragment
console.log(documentFragment); // #document-fragment --> aqui veremos que o document fragment está vazio
const namesArr = ['Name1', 'Name2', 'Name3', 'Name4', 'Name5'];
namesArr.forEach(name => {
const li = document.createElement('li');
liName.textContent = name;
documentFragment.append(liName); //a cada iteração estamos adicionando uma li como filho do document fragment
})
console.log(documentFragment); // neste ponto o document fragment já contém todas as lis dentro dele. Ver NOTA
phrasesList.append(documentFragment); //adicionamos de uma só vez todos os filhos do document fragment (as lis) dentro da lista phrasesList
console.log(documentFragment); // neste ponto o document fragment está vazio porque seus filhos foram adicionados na phrasesList
//NOTA: A seguir se mostra o estado atual do do document fragment no segundo console.log onde podemos ver que contém as lis
// #document-fragment
// <li>Name1</li>
// <li>Name2</li>
// <li>Name3</li>
// <li>Name4</li>
// <li>Name5</li>
Que um elemento seja um nó do DOM, não quer dizer necessariamente que esse elemento já esteja inserido na tela, já que agente pode criar um elemento (ou seja, um nó) usando "document.createElement()" e tempo depois inserir ele na tela (no DOM).
Resumindo, podemos dizer que quando agente presice inserir elementos no DOM, uma forma performática de fazer isso é usando o "documentFragment", porque quando usamos o "documentFragment" agente manipula o DOM mesmo quando agente insere vários elementos nele.
NOTA: O "documentFragment" não tem pai, o seja, a sentença documentFragment.parentElement retorna null. Mas por que? Porque o "documentFragment" foi criado para ser o pai dos elementos que agente quer adicionar no DOM (é só para isso que o "documentFragment" foi criado)
Document fragment
Imaginemos que temos que inserir vários elementos na tela (por exemplo, vários li dentro de uma ul), e que vamos fazer isso inserindo um por um (por exemplo, estamos fazendo isso cada vez que o callback de um forEach é executado), neste caso não é uma boa ideia usar o método "append", já que a cada li inserida, o navegador precisa fazer "reflow" e "repaint" da tela (a tradução de "reflow" e "repaint" é: "refluxo" e "redisenho"). Isto quer dizer que cada vez que uma li seja inserida na tela (ou seja, no DOM), o navegador precisa redesenhar os elementos e recalcular o fluxo dos elementos.
Mas acontece que "manipulação do DOM" é um processo lento e custoso para o processador do usuário, já que quanto mais manipulação do DOM a aplicação executa (estamos imaginando que centos ou Miles de li que tenham que ser inseridas em uma lista), mais o navegador vai exigir do processador por causa desse recalculo de fluxo e a exibição dos elementos. E como sabemos, não é uma boa ideia exigir muito o procesador do usuário.
Para resolver o problema anterior existe uma solução mais performática, e nesta abordagem o DOM é manipulado uma única vez (mesmo que agente tenha que inserir miles de li dentro de uma lista). Então, quando usamos esta solução mais performática, ao invés de inserir as li diretamente no DOM, elas vão ser inseridas em um fragmento de documento (document fragment).
Mas o que é o "document fragment"? Ele é um nó do DOM, ou seja, um elemento do DOM, e dentro dele agente pode colocar elementos que posteriormente agente vai colocar no DOM. Mas acontece que o "document fragment" só existe em memória, portanto, ele não é inserido na tela, e a grande sacada dele ser um nó do DOM que só existe em memória, é que agente pode adicionar outros nodes do DOM como filhos do "document fragment", e essa inserção de nodes no "document fragment" não vai fazer o navegador precisar redesenhar os elementos e recalcular o fluxo dos elementos no DOM, porque como já dito, o document fragment" não "vive" no DOM, ele "vive" na memória. Então, depois que os filhos do "documentFragment" são inseridos no DOM, o "documentFragment" continua estando na memória, mas vazio, porque seus filhos forma inseridos no DOM.
Para entender melhor vamos ver o seguinte exemplo:
Que um elemento seja um nó do DOM, não quer dizer necessariamente que esse elemento já esteja inserido na tela, já que agente pode criar um elemento (ou seja, um nó) usando "document.createElement()" e tempo depois inserir ele na tela (no DOM).
Resumindo, podemos dizer que quando agente presice inserir elementos no DOM, uma forma performática de fazer isso é usando o "documentFragment", porque quando usamos o "documentFragment" agente manipula o DOM mesmo quando agente insere vários elementos nele.
NOTA: O "documentFragment" não tem pai, o seja, a sentença documentFragment.parentElement retorna null. Mas por que? Porque o "documentFragment" foi criado para ser o pai dos elementos que agente quer adicionar no DOM (é só para isso que o "documentFragment" foi criado)