2011/03/13

Optimización de javascript (VIII). Optimizaciones javascript para acceso al DOM

Si hay algún ermitaño que no sabe qué es el DOM, ya puede ir a hacerse el hara-kiri con una cuchara oxidada a otra parte.

Bueno, para optimizar el código de acceso al DOM de una página web, hay unos cuantos consejillos que ayudarán a arañar milésimas a la ejecución de código.

Lo primero que hay que saber es que todas las colecciones DOM están vivas (las devueltas por llamadas del tipo document.images, document.forms, getElementsByTagName(), getElementsByClassName()), de modo que se actualizan cuando el documento subyacente cambia.

Es decir, el siguiente código es un bucle infinito!
var divs = document.getElementsByTagName("div"); // aquí está la consulta a la colección DOM
for(var i=0; i<divs.length; i++) {
var div = document.createElement("div") ;
document.body.appendChild(div);
}
Objetos HTMLCollection
Ahora que estamos centrados en materia, hablemos de los objetos de tipo HTMLCollection, que tienen estas características:
  • Son el resultado de una consulta específica.
  • Parecen arrays, pero no lo son.
  • Tienen la propiedad length
  • La consulta SE RECONSULTA cada vez que se accede al objeto o se consulta su propiedad length. Es mucho más lento que acceder a un array normal (salvo opera y safari). Hasta 68 veces más lento!, avisados quedáis
Recomendaciones
  • Minimizar el acceso a las propiedades.
  • Almacenar el length en una variable local si se accede frecuentemente.
  • Si se necesita acceder frecuentemente a elementos, almacenarlos en un array. El siguiente código se encarga de convertir en un objeto de tipo array un objeto de tipo HTMLCollection


function array(items) { 
    try {
        return Array.prototype.slice.call(items);
    } catch(ex) {
        var i = 0;
        len = items.length;
        result = Array(len);
        while (i < len) 
        {
            result[i] = items[i];
            i++;
        }
    }
    return result;
}


Reflow
Ya hemos hablado de ello, pero es importante tenerlo en cuenta. El reflow es el cálculo de la disposición de los elementos en una página. Se calcula cuando:
  • Se hace la carga inicial de la página
  • Se redimensiona la ventana del navegador
  • Se añaden o eliminan nodos del DOM
  • Se aplican estilos de layout
  • Se obtiene información de layout
Fragmentos de documento (objetos DocumentFragment)
Es una técnica de composición fuera del documento principal con lo que no se producen cambios mientras se modifica. Es un objeto parecido a el objeto document del DOM. No tiene representación visual. Se considera como un hijo del document desde el que ha sido creado. Cuando se  pasa como parámetro a una llamada .addChild(), añade todos sus hijos  en vez de solamente él mismo.

Solo fuerzan un reflow cuando él mismo se añade y no cuando se añade un hijo al fragmento de documento. Es decir, se puede crear un nodo raíz y añadirle hijos de modo que no se fuerza el reflow hasta que no se añade el raíz al documento.

Un ejemplo:

// Ejemplo de DocumentFragment
var list = document.GetElementByClassName(“items”)[0],
           fragment = document.createDocumentFragment(),
           i, item;
for(i=0; i < 10; i++) {
        item = document.createElement(“li”);
        item.innerHTML = “Elemento #” + i;
        fragment.appendChild(item);
}
list.appendChild(fragment); // En este momento se lanza el reflow
Estilos de layout
Son los estilos que cambian la disposición de elementos. P.e.: height, display, fontSize...
Todos ellos provocan un reflow.

Recomendaciones generales:
  • Minimizar los cambios en la propiedad style
  • Definir un CSS con todos los cambios y realizar únicamente cambios en el className
Además del reflow, siempre hay que tener en cuenta el redraw también que es cuando se cambia la representación (no el layout) de un objeto.

Obtención de información de layout
"Pa'habernos matao". Resulta que si consultamos el layout de un elemento, se recalcula la disposición del documento!. Vamos, que perdemos ciclos al consultar propiedades tipo offsetWidth, scrollLeft, getProperty(“display”)...

Recomendaciones:
  • Minimizar el acceso a la información de layout
  • Si un valor se usa más de una vez, almacenarlo en una variable local. Ésta es de perogrullo.
Conclusiones de los dos últimos posts.
  • Cuidado con el uso de objetos de tipo HTMLCollection
  • Realizar las manipulaciones del DOM fuera del documento
  • Cambiar clases CSS en vez de estilos CSS
  • Hay que tener cuidado con cuándo se accede a la información de layout.

No hay comentarios:

Publicar un comentario