Vantagens de createElement sobre innerHTML?

Na prática, quais são as vantagens de usar o createElement sobre o innerHTML? Estou perguntando porque estou convencido de que usar innerHTML é mais eficiente em termos de desempenho e legibilidade / manutenção de código, mas meus colegas de equipe decidiram usar o createElement como a abordagem de codificação. Eu só quero entender como o createElement pode ser mais eficiente.

Existem várias vantagens em usar o createElement vez de modificar o innerHTML (em vez de simplesmente jogar fora o que já existe e substituí-lo) além da segurança, como Pekka já mencionou:

Preserva referências existentes a elementos DOM ao adicionar elementos

Quando você anexa (ou modifica de outra forma) innerHTML , todos os nós DOM dentro desse elemento precisam ser redimensionados e recriados. Se você salvou alguma referência a nós, eles serão essencialmente inúteis, porque eles não são mais aqueles que aparecem mais.

Preserva manipuladores de events anexados a qualquer elemento DOM

Este é realmente apenas um caso especial (embora comum) do último. Definir innerHTML não irá automaticamente append os manipuladores de events aos novos elementos que cria, assim você teria que controlá-los e adicioná-los manualmente. A delegação de events pode eliminar esse problema em alguns casos.

Poderia ser mais simples / rápido em alguns casos

Se você está fazendo muitas adições, você definitivamente não quer continuar reiniciando o innerHTML porque, embora seja mais rápido para mudanças simples, repetir a análise e criar elementos repetidamente seria mais lento. A maneira de contornar isso é construir o HTML em uma string e configurar innerHTML uma vez quando você terminar. Dependendo da situação, a manipulação de string pode ser mais lenta do que apenas criar elementos e anexá-los.

Além disso, o código de manipulação de string pode ser mais complicado (especialmente se você quiser que seja seguro).

Aqui está uma function que uso às vezes que torna mais conveniente usar o createElement .

 function isArray(a) { return Object.prototype.toString.call(a) === "[object Array]"; } function make(desc) { if (!isArray(desc)) { return make.call(this, Array.prototype.slice.call(arguments)); } var name = desc[0]; var attributes = desc[1]; var el = document.createElement(name); var start = 1; if (typeof attributes === "object" && attributes !== null && !isArray(attributes)) { for (var attr in attributes) { el[attr] = attributes[attr]; } start = 2; } for (var i = start; i < desc.length; i++) { if (isArray(desc[i])) { el.appendChild(make(desc[i])); } else { el.appendChild(document.createTextNode(desc[i])); } } return el; } 

Se você ligar assim:

 make(["p", "Here is a ", ["a", { href:"http://www.google.com/" }, "link"], "."]); 

você obtém o equivalente deste HTML:

 

Here is a link.

Embora innerHTML possa ser mais rápido, não concordo que seja melhor em termos de legibilidade ou manutenção. Pode ser mais curto colocar tudo em uma cadeia, mas códigos mais curtos nem sempre são necessariamente mais fáceis de manter.

A concatenação de strings simplesmente não é dimensionada quando elementos DOM dynamics precisam ser criados, pois as aberturas e closings plus e quote tornam-se difíceis de rastrear. Considere estes exemplos:

O elemento resultante é um div com duas extensões internas cujo conteúdo é dynamic. Um dos nomes de class (guerreiro) dentro do primeiro período também é dynamic.

 
John Doe 30th May, 2010

Suponha que as seguintes variables ​​já estejam definidas:

 var personClass = 'warrior'; var personName = 'John Doe'; var date = '30th May, 2010'; 

Usando apenas innerHTML e misturando tudo em uma única string, obtemos:

 someElement.innerHTML = "
" + personName + "" + date + "
";

A bagunça acima pode ser limpa usando substituições de seqüência de caracteres para evitar abrir e fechar seqüências de caracteres toda vez. Mesmo para substituições de texto simples, prefiro usar replace vez de concatenação de strings.

Esta é uma function simples que pega um object de chaves e valores de substituição e os substitui na string. Ele assume que as chaves são prefixadas com $ para denotar que elas são um valor especial. Ele não faz nenhum caso de escape ou manipulação de borda onde $ aparece no valor de substituição, etc.

 function replaceAll(string, map) { for(key in map) { string = string.replace("$" + key, map[key]); } return string; } var string = '
$name$date
'; var html = replaceAll(string, { type: personClass, name: personName, date: date }); someElement.innerHTML = html;

Isso pode ser melhorado separando os atributos, o texto, etc., enquanto se constrói o object para obter um controle mais programático sobre a construção do elemento. Por exemplo, com o MooTools, podemos passar as propriedades do object como um mapa. Isso é certamente mais sustentável, e eu diria que é mais legível também. O jQuery 1.4 usa uma syntax semelhante para passar um mapa para inicializar objects DOM.

 var div = new Element('div'); var person = new Element('span', { 'class': 'person ' + personClass, 'text': personName }); var when = new Element('span', { 'class': 'time', 'text': date }); div.adopt([person, when]); 

Eu não chamaria a abordagem DOM pura abaixo para ser mais legível do que as acima, mas é certamente mais fácil de manter porque não temos que acompanhar as cotações de abertura / fechamento e os numerosos sinais de adição.

 var div = document.createElement('div'); var person = document.createElement('span'); person.className = 'person ' + personClass; person.appendChild(document.createTextNode(personName)); var when = document.createElement('span'); ​when.className = 'date​​​​​​'; when.appendChild(document.createTextNode(date)); ​div.appendChild(person); div.appendChild(when); 

A versão mais legível provavelmente resultaria do uso de algum tipo de modelo JavaScript .

 
<%= name %> <%= date %>
var div = $("#personTemplate").create({ name: personName, type: personClass, date: date });

Usuário bobince coloca um número de cons muito, muito bem em sua crítica de jQuery .

… Além disso, você pode fazer um div dizendo $ (” + mensagem + ”) em vez de ter que mexer com o document.createElement (‘div’) e os nós de texto. Viva! Apenas … espere. Você não escapou desse HTML e provavelmente acabou de criar uma falha de segurança de script entre sites, apenas no lado do cliente dessa vez. E depois que você gastou tanto tempo limpando seu PHP para usar htmlspecialchars no lado do servidor, também. Que pena. Ah, bem, ninguém realmente se preocupa com correção ou segurança, não é?

jQuery não é totalmente culpado por isso. Afinal, a propriedade innerHTML existe há anos e já se mostrou mais popular que o DOM. Mas a biblioteca certamente encoraja esse estilo de codificação.

Quanto ao desempenho: InnerHTML definitivamente será mais lento, porque ele precisa ser analisado e convertido internamente em elementos DOM (talvez usando o método createElement ).

O InnerHTML é mais rápido em todos os navegadores, de acordo com o benchmark quirksmode fornecido pelo @Pointy.

Quanto à legibilidade e facilidade de uso, você vai me encontrar escolhendo innerHTML sobre createElement qualquer dia da semana na maioria dos projetos. Mas, como você pode ver, há muitos pontos falando de createElement .

Você deve usar createElement se quiser manter referências em seu código. InnerHTML às vezes pode criar um bug que é difícil de detectar.

Código HTML:

 

sample text about anything

Código JS:

 var test = document.getElementById("test"); test.style.color = "red"; //1 - it works document.getElementById("parent").innerHTML += "whatever"; test.style.color = "green"; //2 - oooops 

1) você pode mudar a cor

2) você não pode mudar de cor ou qualquer outra coisa, porque na linha acima você adicionou algo ao innerHTML e tudo é recriado e você tem access a algo que não existe mais. Para alterá-lo, você precisa novamente getElementById .

Você precisa lembrar que isso também afeta qualquer evento. Você precisa reaplicar os events.

InnerHTML é ótimo, porque é mais rápido e mais fácil de ler, mas você deve ter cuidado e usá-lo com cuidado. Se você sabe o que está fazendo, vai ficar bem.