2009-04-10 15 views
20

Estoy viendo un problema que necesita crear un bloque complejo de divs una vez para cada elemento en un conjunto de ~ 100 elementos.JavaScript: ¿Es mejor usar innerHTML o (muchas) llamadas a createElement para agregar una estructura div compleja?

Cada elemento individual es idéntico excepto por el contenido, y se ven (en HTML) o menos así:

<div class="class0 class1 class3"> 
<div class="spacer"></div> 
<div id="content">content</div> 
<div class="spacer"></div> 
<div id="content2">content2</div> 
<div class="class4">content3</div> 
<div class="spacer"></div> 
<div id="footer">content3</div> 
</div> 

Yo tampoco podía:

1) Crear todos los elementos como innerHTML con una cuerda concatenación para agregar el contenido.

2) Use createElement, setAttribute y appendChild para crear y agregar cada div.

La opción 1 obtiene un archivo ligeramente más pequeño para descargar, pero la opción 2 parece ser un poco más rápida de renderizar.

¿Aparte del rendimiento hay una buena razón para ir por una ruta u otra? ¿Algún problema de navegador/gremlins de rendimiento que deba probar?

... o debería probar la plantilla y el enfoque clónico?

Muchas gracias.

+0

Véase también [¿Es posible anexar a innerHTML sin destruir los detectores de eventos descendientes?] (Http://stackoverflow.com/q/595808/1048572) – Bergi

Respuesta

2

Personalmente, uso innerHTML porque es a lo que estoy acostumbrado y para algo como esto, los métodos W3C agregan mucho desorden al código.

Sin embargo, como una forma posible de reducir el número de divisiones, ¿hay alguna razón por la que esté utilizando elementos espaciadores en lugar de solo editar los márgenes en los divisores de contenido?

+0

Preguntas a OP debe haber en los comentarios sobre el original pregunta, no parte de tu respuesta. – Blazemonger

1

No creo que haya mucho para elegir entre ellos. En los viejos tiempos (IE6, FF1.5), innerHTML era más rápido (benchmark), pero ahora no parece haber una diferencia notable en la mayoría de los casos.

De acuerdo con mozilla dev. docs hay algunas situaciones donde el comportamiento de innerHTML varía entre los navegadores (especialmente en las tablas), por lo que createElement le dará más consistencia, pero innerHTML suele ser menos de tipo.

0

Como sé, la manera más rápida es evadir la edición DOM siempre que sea posible. Eso significa que es mejor crear una gran cadena y luego ponerlo en innerHTML. Pero hay una observación para esto: no haga demasiadas operaciones en grandes cadenas, es más rápido usar una matriz de cadenas y luego unirlas todas.

17

Ninguno. Utilizar una biblioteca como jQuery, Prototype, Dojo o mootools porque ambos de estos métodos son plagado de problemas:

Los autores de las principales bibliotecas de JavaScript han pasado mucho tiempo y tienen sistemas de seguimiento de errores enteras para asegurarse de que cuando se llama a sus herramientas de modificación de DOM funcionan realmente.

Si está escribiendo una biblioteca para competir con las herramientas anteriores (y buena suerte si es así), entonces elegiría el método basado en el rendimiento, y innerHTML siempre ha ganado en el pasado, y Como innerHTML es un método nativo, es una apuesta segura que seguirá siendo el más rápido.

9

altCognito hace un buen punto: usar una biblioteca es el camino a seguir. Pero si lo hiciera a mano, usaría la opción n. ° 2: crear elementos con métodos DOM. Son un poco feos, pero puedes crear una función de fábrica de elementos que oculte lo feo. La concatenación de cadenas de HTML también es fea, pero es más probable que tenga problemas de seguridad, especialmente con XSS.

Sin embargo, definitivamente no agregaría los nuevos nodos de forma individual. Yo usaría un DOM DocumentFragment. Agregar nodos a documentFragment es mucho más rápido que insertarlos en la página activa. Cuando termines de construir tu fragmento, simplemente se inserta todo de una vez.

John Resig explains it much better than I could, pero básicamente se acaba de decir:

var frag = document.createDocumentFragment(); 
frag.appendChild(myFirstNewElement); 
frag.appendChild(mySecondNewElement); 
...etc. 
document.getElementById('insert_here').appendChild(frag); 
0

Para un problema tan complejo como este, que suelen utilizar los métodos innerHTML porque son a) fácil de leer y modificar el código en cuanto b) más conveniente usar en bucles Como dice la publicación principal, lamentablemente fallan en IE (6,7,8) en <table>, <thead>, <tbody>, <tr>, <tfoot>, <select>, <pre> elementos.

2

Ya que menciona la plantilla y el clon, que pueden estar interesados ​​en esta pregunta:

Otra opción es utilizar un envoltorio de DOM, como DOMBuilder:

DOMBuilder.apply(window); 
DIV({"class": "class0 class1 class3"}, 
    DIV({"class": "spacer"}), 
    DIV({id: "content"}, "content"), 
    DIV({"class": "spacer"}), 
    DIV({id: "content2"}, "content2"), 
    DIV({"class": "class4"}, "content3"), 
    DIV({"class": "spacer"}), 
    DIV({id: "footer"}, "content3") 
); 

Personalmente, si cada artículo va a necesitar el exacto mismo s tructure creado iría con el enfoque de clonación. Si hay alguna lógica involucrada en la creación de la estructura a la que irá el contenido, preferiría mantener algo como lo anterior que jugar con cadenas. Si ese enfoque resultara demasiado lento, recurriría a innerHTML.

-1

1) Cree todos los elementos como innerHTML con concatenación de cadenas para agregar el contenido.

2) Use createElement, setAttribute y appendChild para crear y agregar cada div.

3) compromiso. Cree todos los elementos de una vez como innerHTML (que evita mucha lentitud en la manipulación de listas childnode), luego escriba el contenido que cambia en cada elemento usando acceso de datos/atributos (evitando todos esos desagradables problemas de tener que escaparse de HTML) . p.ej. algo así como:

var html= (
    '<div class="item">'+ 
     '<div class="title">x</div>'+ 
     '<div class="description">x</div>'+ 
    '</div>' 
); 
var items= [ 
    {'id': 1, 'title': 'Potato', 'description': 'A potato'}, 
    {'id': 2, 'title': 'Kartoffel', 'description': 'German potato'}, 
    // ... 100 other items ... 
]; 

function multiplyString(s, n) { 
    return new Array(n+1).join(s); 
} 

var parent= document.getElementById('itemcontainer'); 
parent.innerHTML= multiplyString(html, items.length); 
for (var i= 0; i<items.length; i++) { 
    var item= items[i]; 
    var node= parent.childNodes[i]; 

    node.id= 'item'+item.id; 
    node.childNodes[0].firstChild.data= item.title; 
    node.childNodes[1].firstChild.data= item.description; 
} 

También se puede combinar con la sugerencia de Neall sobre DocumentFragments.

12

Depende de lo que sea "mejor" para ti.

Rendimiento

Desde el punto de vista del rendimiento, createElement + appendChild gana por mucho. Eche un vistazo a this jsPerf que creé cuando comparé ambos y los resultados golpearon en la cara.

innerHTML: ~120 ops/sec 
createElement+appendChild: ~145000 ops/sec 

(en mi Mac con Chrome 21)

Además, innerHtml Página de activación de reflujo.

En Ubuntu con Chrome 39 tests obtener resultados similares

innerHTML: 120000 ops/sec 
createElement: 124000 ops/sec 

probablemente alguna optimización tener lugar. En Ubuntu con el navegador basado QtWebKit Arora (wkhtml también QtWebKit) resultados son

innerHTML: 71000 ops/sec 
createElement: 282000 ops/sec 

parece createElement más rápido en promedio

Mantainability

Desde un punto de vista mantainability, creo que las plantillas de cuerdas que ayudan mucho. Uso Handlebars (que me encanta) o Tim (para proyectos que necesitan huellas más pequeñas). Cuando "compila" (prepara) su plantilla y está lista para anexarla al DOM, usa innerHTML para agregar la cadena de la plantilla al DOM. En el truco que hago para evitar reflow es createElement para un contenedor y en ese elemento contenedor, pon la plantilla con innerHTML. Todavía estoy buscando una buena manera de evitar innerHTML en absoluto.

Compatibilidad

Usted no tiene que preocuparse aquí, ambos métodos son totalmente compatibles con una amplia gama de navegadores (a diferencia de altCognito dice). Puede consultar los gráficos de compatibilidad para createElement y appendChild.

+0

Su enlace jsPerf está roto. – Blazemonger

2

Ninguno. Use cloneNode.

var div = document.createElement('div'); 
var millionDivs = [div]; 
while (millionDivs.length < 1e6) millionDivs.push(div.cloneNode()) 
+0

Olvidó explicar por qué. – Blazemonger

+1

@Blazemonger ¿Por qué? Las implementaciones del navegador tienden a ser más rápidas. – bjb568

Cuestiones relacionadas