Javascript et l’utilisation de la propriété offsetWidth

Par Vincent Beaulieu, vendredi 16 avril 2010 09:41
Catégorie : Programmation

J'ai voulu dernièrement optimiser une fonction JavaScript qui était plutôt gourmande en temps d'exécution.

Suite à plusieurs tentatives, j'ai réalisé que c'était l'utilisation des propriétés offsetWidth et offsetHeight des éléments qui était en cause. Elles sont utilisées pour connaître la taille réelle d'un élément. Avec offsetTop et offsetLeft, nous avons aussi le même problème.

Après avoir exposé ma solution à Tommy, il m'est arrivé avec une explication logique à ce problème.

Voici le code qui causait les problèmes de performance :

   1: ContainerBox.prototype.PositionBox = function(element, borderColor, width, borderStyle) {
   2:  
   3:         var pos = J(element).offset();
   4:  
   5:         this.TopBar.style.top = pos.top;
   6:         this.TopBar.style.left = pos.left;
   7:         this.TopBar.style.width = element.offsetWidth + (width);
   8:         this.TopBar.style.display = "";
   9:         this.TopBar.style.borderTop = borderStyle + " " + width + "px " + borderColor;
  10:         this.TopBar.style.visibility = "visible";
  11:         this.TopBar.setAttribute("BorderedItemID", element.id);
  12:  
  13:  
  14:         this.LeftBar.style.top = pos.top;
  15:         this.LeftBar.style.left = pos.left + 1;
  16:         this.LeftBar.style.height = element.offsetHeight;
  17:         this.LeftBar.style.display = "";
  18:         this.LeftBar.style.visibility = "visible";
  19:         this.LeftBar.style.borderLeft = borderStyle + " " + width + "px " + borderColor;
  20:         this.LeftBar.setAttribute("BorderedItemID", element.id);
  21:  
  22:  
  23:         this.BottomBar.style.top = pos.top + element.offsetHeight - 1;
  24:         this.BottomBar.style.left = pos.left + 1;
  25:         this.BottomBar.style.width = element.offsetWidth;
  26:         this.BottomBar.style.display = "";
  27:         this.BottomBar.style.visibility = "visible";
  28:         this.BottomBar.style.borderTop = borderStyle + " " + width + "px " + borderColor;
  29:         this.BottomBar.setAttribute("BorderedItemID", element.id);
  30:  
  31:  
  32:         this.RightBar.style.top = pos.top;
  33:         this.RightBar.style.left = pos.left + element.offsetWidth;
  34:         this.RightBar.style.height = element.offsetHeight;
  35:         this.RightBar.style.display = "";
  36:         this.RightBar.style.visibility = "visible";
  37:         this.RightBar.style.borderLeft = borderStyle + " " + width + "px " + borderColor;
  38:         this.RightBar.setAttribute("BorderedItemID", element.id);
  39: }

En gros, cette fonction sert à afficher le contour des zones dans un éditeur.  À priori, ce qui est fait là est assez simple, je positionne 4 divs au dessus d'une zone de l'éditeur. Pour le cas particulier que je testais, il y avait 72 zones pour lesquelles nous devrions faire afficher le contour, ce qui prenait 8 secondes à exécuter.

Le problème c'est que chacun de ces divs est déjà ajouté au html de ma page donc chaque changement de style que je fais va potentiellement affecter le offsetWidth ou le offsetHeight.

Par conséquent, à chaque fois qu'un style est modifié, IE est flagé pour recalculer les offsets en fonction des nouveaux styles, et ce même si ça n'a pas réellement affecté le offsetWidth d'un élément.

Pour optimiser, il suffisait donc de mettre mes offsets dans une variable au début de la fonction (avant le code qui modifie les styles) et d’utiliser ces variables pour changer les styles. Après optimisation, la même opération prend moins d’une seconde!

Voici l'explication trouvée par Tommy dans sa version originale :

You just caused two reflows to happen, since asking for offsetWidth forced the element to reflow in order to answer you question (because it had a pending change to style).
This is the real performance bottleneck to be wary of. Browsers are smart about avoiding reflows when they can, but if you create code that forces a reflow in order to answer a question, then you can create severe performance

Merci à Tommy!

Ajouter un commentaire

no avatar
Entrez votre nom, alias ou adresse de courriel.
Nous vous incarnerons à partir des services ici-bas.
 

(Affichera votre icône Gravatar)

  Country flag

biuquote
Loading