Desenvolvimento

3 dez, 2014

Como inserir elementos no HTML com JavaScript

Publicidade

Quando eu comecei a aprender JavaScript, aprendi a usar a propriedade innerHTML usando innerHTML. É muito fácil colocar elementos no DOM, porém é complicado de usar por exemplo quando você quer colocar um elemento no final de um elemento ou no início. Neste artigo, vou mostrar como inserir elementos no DOM usando innerHTML, insertBefore e insertAdjacentHTML fazendo um prepend como exemplo.

HTML para os exemplos

Nos exemplos deste artigo, vou usar apenas este HTML como exemplo:

<div class="wrapper">Conteúdo</div>

Usando innerHTML

Vou exemplificar como fazer um prepend com innerHTML. O prepend significa colocar HTML antes de todo o conteúdo de um elemento.

 

var $wrapper = document.querySelector('.wrapper'),
    // Pega a string do conteúdo atual
    HTMLTemporario = $wrapper.innerHTML,
    // Novo HTML que será inserido
    HTMLNovo = 'Teste prepend com innerHTML </br>';
 
// Concatena as strings colocando o novoHTML antes do HTMLTemporario
HTMLTemporario = HTMLNovo + HTMLTemporario;
 
// Coloca a nova string(que é o HTML) no DOM
$wrapper.innerHTML = HTMLTemporario;

Vejo um demo no codepen.

Isso funciona bem, porém há algumas observações de segurança e/ou do innerHTML – ele pode retornar algo que você não espera; por exemplo, você espera um <, e ele retorna um &lt. No MDN, você tem mais informações sobre isso.

Usando DOM Node API

Node API é uma interface com vários métodos para manipularmos o DOM da “maneira correta”. Isso porque o innerHTML não faz parte da especificação, e inserir elementos no DOM usando a Node API pode ser um pouco complicado (pelo menos para mim), porque tem muitos métodos e propriedades, mas é algo muito mais completo do que o innerHTML.

A DOM API é complexa, porque é grande, tem vários métodos e propriedades, e pode ser que seja difícil aprender por causa dessa variedade de nomes complicados, além de ser difícil de encontrá-la no meio das APIs do DOM, porque o DOM, por si só, é uma representação de um documento (HTML), e ele tem vários métodos para você manipulá-lo. Esses métodos e propriedades são divididos em algumas APIs – por exemplo, tem Comment API para comentários no DOM, uma Node API para Node e uma Element API (que parece que é a mesma coisa que um Node, mas não é), uma Event API, EventTarget API, uma API para NodeList, e muitas outras.

Eu optei por um aprendizado “orgânico”, isto é, apenas dei uma passada em todo o conteúdo da MDN a fim de ao menos ter uma pequena noção do que existe e, conforme vou precisando de algo, vou aprendendo, porque se eu ler tudo de uma vez só, provavelmente no próximo dia já não vou mais lembrar de nada. Então, esse aprendizado orgânico, que é sob demanda, eu considero melhor para o meu caso.

Para seguir o exemplo do prepend que fiz com innerHTML, usei o método insertBefore, e as propriedade firstChild e lastChild do Node API:

// Seleciona o elemento no DOM
var $wrapper = document.querySelector('.wrapper'),
 
    // Seleciona o primeiro Node filho do elemento
    firstChild = $wrapper.firstChild,
 
    // Cria uma string de texto
    HTMLNovo = document.createTextNode('Inserido usando DOM API'),
 
    // Cria um Node de um BR
    HTMLBr = document.createElement('br');
 
// Insere o texto antes do primeiro Node do elemento
$wrapper.insertBefore(HTMLNovo, firstChild);
 
// Insere o BR, antes do ultimo node do elemento
$wrapper.insertBefore(HTMLBr, $wrapper.lastChild);

Vejo um demo no codepen.

A parte chata de usar Node API,é que você tem que criar os elementos, e isso pode se transformar em uma tarefa chata. Veja que eu tenho que criar o node BR para colocá-lo no DOM, o que dá muito trabalho.

Repare que para inserir o br, depois do primeiro node que agora é a HTMLNovo, e antes do texto antigo, eu uso o lastChild, o que pode ser um sério problema – imagine um caso em que aquele não fosse o último Node, isso complicaria.

Podemos criar uma versão melhor disso usando DocumentFragment:

// Seleciona o elemento no DOM
var $wrapper = document.querySelector('.wrapper'),
 
    // Cria uma string de texto
    HTMLNovo = document.createTextNode('Inserido usando DOM API'),
 
    // Cria um "fragmento de documento" oO
    fragment = new DocumentFragment();
 
// Insere no fragmento o novo texto
fragment.appendChild(HTMLNovo);
 
// Insere no documento o br
fragment.appendChild(document.createElement('br'));
 
// Insere o fragmento antes do conteúdo atual do elemento
$wrapper.insertBefore(fragment, $wrapper.firstChild);

Vejo um demo no codepen.

Basicamente, no DocumentFragment, é como se tivéssemos um mini document, apenas para trabalharmos ali, naquele momento, e a qualquer momento podemos colocá-lo no DOM.

Mas criar o elemento BR continua sendo algo meio estranho/chato, não acha? Por que diabos eu tenho que criar um node br? Seria mais simples com innerHTML e seria ótimo se pudéssemos adicionar elementos a um DocumentFragment com innerHTML, porém, por algum motivo que eu não conheço, não temos como usar o innerHTML (provavelmente porque o innerHTML não ESTAVA na especificação, mas ele foi adicionado à especificação) em um DocumentFragment, então não temos como mesclar o melhor dos dois mundos – a facilidades do innerHTML e a precisão do insertBefore, appendendChild.

Usando insertAdjacentHTML

Falando sobre a combinação de innerHTML e Node API, o método insertAdjacentHTML é o que une o melhor das duas formas de inserir elementos no DOM, porque nele você pode passar uma string que representa um HTML, e você não precisa fazer a ‘gambiarra’ para fazer um prepend, como foi feito com innerHTML no primeiro exemplo. Olha como fica nosso prepend com este insertAdjacentHTML:

// Seleciona o elemento no DOM
var $wrapper = document.querySelector('.wrapper'),
 
    // String de texto
    HTMLNovo = 'Inserido usando DOM API <br>';
 
// Insere o texto antes do conteúdo atual do elemento
$wrapper.insertAdjacentHTML('afterbegin', HTMLNovo);

Vejo um demo no codepen.

O método insertAdjacentHTML, recebe dois parâmetros, são eles:

Primeiro parâmetro, position

O primeiro parâmetro indica onde o método deve colocar o HTML com relação ao elemento atual. Há quatro valores que você pode passar aqui.

  • beforebegin: Insere o HTML antes do elemento
  • afterend: Insere o HTML depois do elemento
  • afterbegin: Insere o HTML depois do início do elemento, antes do início de qualquer conteúdo do elemento
  • beforeend: Insere o HTML antes do fim do elemento, depois de qualquer conteúdo dentro do elemento

Caso minha explicação tenha ficado complicada de entender, fiz uma ilustração para ajudar. O quadrado cinza é o elemento, e onde estão os textos azuis o local onde o método adiciona o HTML com aqueles nomes.

insertadjacenthtml-position

Segundo parâmetro, content

Este é o parâmetro que você passa para a string que será adicionada ao HTML, simples assim.

Nota: Antigamente, apenas o IE tinha esse método implementado, não existia uma especificação dele, assim como o innerHTML – só agora é que eles estão sendo padronizados.

Suporte de browsers

O método insertAdjacentHTML é suportado por todos os browsers modernos (referência MDN), incluindo versões antigas do IE (até IE6), e uma boa notícia é que esse método foi adicionado à especificação DOM Parsing and Serialization, o que indica que talvez logo se torne uma padrão.

Já para mobile, no há informações no MDN – apenas diz que o Firefox Mobile suporta m,as não diz nada sobre os outros. Acredito que tem um bom suporte dos browsers mobile (se você conhecer uma boa fonte sobre isso, compartilhe comigo nos comentários).

Problemas ao usar o insertAdjacentHTML

Neste texto, John Resig falou sobre esse método e alguns testes que ele fez com o método. No artigo, ele diz que esse método não funciona com todos os elementos no IE6, tendo problemas especificamente com table, tbody, thead, e tr. E que não funciona com XML (mas eu espero que você não tenha que desenvolver para o IE6 hoje, nem para o IE7).

Curiosidade

Por incrível que pareça, a primeira aparição do termo insertAdjacentHTML com referência nos resultados do Google foi de um cara em 15/01/2000, passando um código do tipo “copia e cola”, em que o insertAdjacentHTML está no meio do código. Achei engraçado, porque quanto escrevo este texto, em janeiro, fez exatamente 14 anos desde essa primeira aparição (URL do site, URL do site no Web Archive).

Links de referência