Ardilla Quio Ardilla Quio

04 de Abril de 2012

jQuery, optimizando para aumentar el rendimiento: el DOM

En artículos anteriores vimos cómo optimizar los selectores en jQuery y otras formas de optimizar jQuery para conseguir un mejor rendimiento de nuestro código. En este artículo trataremos sobre la manipulación del DOM, una acción que es tan costosa que debe tratarse con mucho cuidado.

Recordar que, para la mayoría de proyectos no es necesario rehacer el código pero que siempre podemos tener en cuenta estas pautas en adelante.

Minimizar la manipulación del DOM

La manipulación del DOM (append(), prepend(), after(), ...) es una acción muy costosa ya que requiere del navegador que vuelva a calcular y volver a mostrar el árbol de elementos. Por esta razón, hay que intentar realizar este tipo de acciones lo menos posible. Si, por ejemplo, queremos añadir un array de imágenes a una lista, quizá se nos ocurriría algo así:

var arr = ["imagen_1.jpg", "imagen_2.jpg", "otra_imagen_1.jpg", "otra_imagen_2.jpg", "imagen_3.jpg",
           "otra_imagen_3.jpg", "imagen_4.jpg", "imagen_5.jpg", "imagen_6.jpg", "imagen_7.jpg"];
var list = $("#list");
$.each(arr, function(count, item)
{
	var img = "<li><img src=\"" + item + "\"></li>";
	list.append(img);
});

Sin embargo, hacer tantas inserciones en el DOM es muy lento. Sería mucho mejor acumular primero todo lo que queremos añadir y hacer una sola inserción, de este modo:

var arr = ["imagen_1.jpg", "imagen_2.jpg", "otra_imagen_1.jpg", "otra_imagen_2.jpg", "imagen_3.jpg",
           "otra_imagen_3.jpg", "imagen_4.jpg", "imagen_5.jpg", "imagen_6.jpg", "imagen_7.jpg"];
var tmp = "";
$.each(arr, function(count, item)
{
	var tmp += "<li><img src=\"" + item + "\"></li>";
});
$("#list").append(tmp);

Podemos comparar la diferencia de rendimiento entre una o múltiples inserciones en el DOM en jsPerf.

detach()

Si es necesario realizar muchas tareas sobre un nodo, lo mejor es primero sacarlo del DOM con detach(), hacer todo lo que sea necesario sobre él y luego volver a insertarlo donde estaba. Con eso lo que hacemos es que el navegador trabaje con el elemento en memoria (más rápido) y evitamos que tenga que recalcular todo el árbol de elementos en cada acción.

var table = $("#table");
var parent = table.parent();
table.detach();

// Aquí realizaremos todas las tareas necesarias en la tabla fuera del DOM

parent.append(table);

Podemos comparar la diferencia de rendimiento entre usar detach() o no en jsPerf.

También deberíamos tener en cuenta a la hora de usar o no detach() el efecto de que el navegador repinte la página para cada acción que realizamos si no lo usamos o que lo haga una sola vez si lo usamos.

appendTo()

Muchas veces necesitamos seguir usando un elemento después de crearlo. Es habitual ver este esquema:

$("body").append("<div class=\"elemento\"></div>");
$(".elemento").click(function()
{
	// Código
});

Esto obliga al navegador a crear un elemento, recalcular todo el DOM y luego volver a recorrerlo para encontrarlo. Sería mucho mejor utilizar la función appendTo(), que hace lo mismo que append(), pero devuelve el elemento insertado. Sería de esta forma:

$("<div class=\"elemento\"></div>").appendTo("body").click(function()
{
	// Código
});

data()

jQuery tiene la función data() para almacenar datos que podamos necesitar en los elementos del DOM. Es mucho mejor utilizar este recurso que almacenar los datos en los elementos y recuperarlos con las funciones text() o html().

var el = $("#elemento");
el.data("datos", 5);
var datos = el.data("datos");

Esta es la forma habitual de usarlo, pero utilizar $.data() es más rápido.

var el = $("#elemento");
$.data(el, "datos", 5);
var datos = $.data(el, "datos");

Podemos comparar la diferencia de rendimiento entre usar $.data() y $.fn.data() en jsPerf.

Aunque $.data() es más rápida que $.fn.data(), no siempre puede ser usado, ya que no acepta expresiones como parámetro sino que hay que pasarle un elemento.

Puedes leer más sobre este tema en nuestro artículo jQuery, optimizando para aumentar el rendimiento: uso excesivo.

3 comentarios

Miguel

17/08/2015 17:10:28

Buenas colegas tengo una consulta con un remove.

Tratare de explicar lo mas simple posible que es lo que pasa.

Supongamos que tengo estos divs

<div id='contenedor'>

<div id='item1'>Hola</div>

<div id='item2'>Chau</div>

<div id='item3'>Eh!</div>

</div>

puedo por ejemplo sacar un alert del item1 y funcionaria.

alert($('#item1').html());

El problema se da cuando quiero remover estos items y recargarlos.

Hago lo siguiente.

$('#contenedor').children().remove();

$('#contenedor').append('<div id='item1'>Soy Nuevo</div>');

el resultado nuevo seria

<div id='contenedor'>

<div id='item1'>Soy Nuevo</div>

</div>

Cuando quiero usar el nuevo valor de item1 me devuelve 'undefined'

alert($('#item1').html());

Alguien sabe porque podria darse esto??

Desde ya gracias por la ayuda

Anónimo

15/02/2014 05:43:41

yo siempre agregaba los elementos dentro del $.each() :O gracias por la informacion

Elias

25/07/2012 16:11:00

Muy interesante ... gracias por los tips...

Comentario anónimo
Comentar como usuario