javascript html innerhtml anti-patterns

javascript - ¿Por qué es "element.innerHTML+=" código incorrecto?



anti-patterns (6)

Me han dicho que no agregue cosas usando element.innerHTML += ... así:

var str = "<div>hello world</div>"; var elm = document.getElementById("targetID"); elm.innerHTML += str; //not a good idea?

¿Qué tiene de malo ?, ¿qué otras alternativas tengo?


Acabo de aprender de la peor manera por qué innerHTML es malo, en este código a continuación cuando configura innerHTML Chrome pierde el evento onclick jsFiddle

var blah = document.getElementById(''blah''); var div = document.createElement(''button''); div.style[''background-color''] = ''black''; div.style.padding = ''20px;''; div.style.innerHTML = ''a''; div.onclick = () => { alert(''wtf'');}; blah.appendChild(div); // Uncomment this to make onclick stop working blah.innerHTML += '' this is the culprit''; <div id="blah"> </div>


Cada vez que se establece innerHTML , se debe analizar el HTML, se debe construir un DOM e insertarlo en el documento. Esto lleva tiempo.

Por ejemplo, si elm.innerHTML tiene miles de divs, tablas, listas, imágenes, etc., llamar .innerHTML += ... hará que el analizador vuelva a analizar todo eso una vez más. Esto también podría romper las referencias a elementos DOM ya construidos y causar otro caos. En realidad, todo lo que quiere hacer es añadir un único elemento nuevo al final.

Es mejor llamar a appendChild :

var newElement = document.createElement(''div''); newElement.innerHTML = ''<div>Hello World!</div>''; elm.appendChild(newElement);​​​​​​​​​​​​​​​​

De esta manera, los contenidos existentes de elm no se analizan de nuevo.

NOTA: Es posible que [algunos] navegadores sean lo suficientemente inteligentes como para optimizar el operador += y no volver a analizar los contenidos existentes. No he investigado esto.


La alternativa es .createElement() , .textContent y .appendChild() . Agregar con += es solo un problema si se trata de una gran cantidad de datos.

Demostración: http://jsfiddle.net/ThinkingStiff/v6WgG/

Guión

var elm = document.getElementById( ''targetID'' ), div = document.createElement( ''div'' ); div.textContent = ''goodbye world''; elm.appendChild( div );

HTML

<div id="targetID">hello world</div>


La respuesta de Mike es probablemente la mejor, pero otra consideración es que se trata de cadenas. Y la concatenación de cadenas en JavaScript puede ser realmente lenta, especialmente en algunos navegadores más antiguos. Si solo está concatenando pequeños fragmentos de HTML, entonces probablemente no se note, pero si tiene una parte importante de la página a la que está agregando algo repetidamente, podría ver una pausa notable en el navegador.


Sí, elm.innerHTML += str; es una muy mala idea

La típica respuesta de "navegador tiene que reconstruir DOM" realmente no le hace justicia:

  1. En primer lugar, el navegador debe examinar cada elemento de olmo, cada una de sus propiedades y todos sus textos, comentarios y nodos de proceso, y escapar de ellos para crear una cadena.

  2. Luego tienes una cadena larga a la que anexas. Este paso está bien.

  3. En tercer lugar, cuando configura innerHTML, el navegador debe eliminar todos los elementos, propiedades y nodos que acaba de pasar.

  4. Luego analiza la cadena, construye a partir de todos los elementos, propiedades y nodos que acaba de destruir, para crear un nuevo fragmento de DOM que es básicamente idéntico.

  5. Finalmente, conecta los nuevos nodos, y el navegador tiene que diseñar todo. Esto puede evitarse (ver la alternativa a continuación), pero incluso si los nodos adjuntos requieren un diseño, los nodos antiguos tendrían sus propiedades de diseño en caché en lugar de volver a calcularse desde cero.

  6. ¡Pero aún no ha terminado! El navegador también tiene que reciclar los nodos antiguos mediante el escaneo de todas las variables de JavaScript.

Problemas:

  • Algunas propiedades pueden no reflejarse en HTML, por ejemplo, el valor actual de <input> se perderá y se restablecerá al valor inicial en el HTML.

  • Si tiene controladores de eventos en los nodos antiguos, se destruirán y tendrá que volver a conectarlos.

  • Si su código js hace referencia a cualquier nodo antiguo, no se destruirá, sino que quedará huérfano. Pertenecen al documento pero ya no están en el árbol DOM. Cuando su código acceda a ellos, no puede pasar nada o puede generar un error.

  • Ambos problemas significan que no es compatible con los complementos js: los complementos pueden adjuntar manejadores o recordar nodos antiguos y causar pérdida de memoria.

  • Si adquiere el hábito de manipular DOM con innerHTML, puede cambiar accidentalmente las propiedades o hacer otras cosas que no desea.

  • Cuantos más nodos tenga, cuanto más ineficiente sea esto, mayor será el consumo de batería para nada.

En resumen, es ineficiente, es propenso a errores, simplemente es flojo y desinformado.

La mejor alternativa es Element.insertAdjacentHTML , que no he visto mencionar otras respuestas:

elm.insertAdjacentHTML( ''beforeend'', str )

Casi el mismo código, sin problemas internos de HTML. Sin reconstrucción, sin controlador perdido, sin reinicio de entrada, menos fragmentación de memoria, sin malos hábitos, sin creaciones y asignaciones de elementos manuales.

Le permite inyectar cadena html en elementos en una línea, incluidas propiedades, e incluso permite inyectar elementos compuestos y múltiples. Su velocidad está optimised ; en la prueba de Mozilla es 150 veces más rápido.

En caso de que alguien le diga que no es un navegador cruzado, es tan útil que es un standard HTML5 y está disponible en todos los navegadores .

No escriba nunca elm.innerHTML+= nuevamente.


Si el usuario tiene versiones anteriores de IE (o quizás las más nuevas también, no lo han intentado), innerHTML en una td causará problemas. Los elementos de tabla en IE son de solo lectura, tsk tsk tsk.