11548 sujets

JavaScript, DOM et API Web HTML5

Salut à tous!

J'était bien content de ma fonction addEvent :
function addEvent(obj, type, fn, par){
  if(obj.addEventListener){
    obj.addEventListener(type, function(event){        
      return fn.call(obj, event, par);
    }, false );
  }else if(obj.attachEvent){
    obj.attachEvent("on"+type, function(e){
      if (!e) var e = window.event;   
      return fn.call(obj, e, par);
    });
  }
};


Jusqu'à ce que Julien Royer me parle du problème de fuite de mémoire Smiley biggol .

Comme j'utilise cette fonction de partout c'est plutôt gênant.

Aprés la lecture de ce document j'ai trouvé la solution suivante :
function addEvent(obj, type, fn, par){
  if(obj.addEventListener){
    obj.addEventListener(type, function(event){        
      return fn.call(obj, event, par);
    }, false );
  }else if(obj.attachEvent){
    var ieHandler = function(e){
      if (!e) var e = window.event;   
      return fn.call(obj, e, par);
    };
    obj.attachEvent("on"+type, ieHandler);
    window.attachEvent("onunload",cleanMemory);
    function cleanMemory(){
      obj.detachEvent("onclick",ieHandler);
      window.detachEvent("onunload",cleanMemory);
      obj = null;
    }
  }
};


J'ai comparé avec d'autre solutions types "ramasses miettes" qui crée un tableau des événements pour les détacher ensuite, mais je trouve celle ci plus simple, car il n'y besoin ni de stocker ni d'appeler une autre fonction.

Est ce que ce code est bon?

Il y a t'il des solutions plus simple en pouvant garder la fonction addEvent avec les mêmes propriétés (passage du this et d'un paramètre optionnel)?
Modifié par matmat (01 Jul 2008 - 03:24)
l'idée est très bonne, mais je serais plus partisan d'utiliser des fonctions codées par dean edwards ou crockford
Je connaissais les leak mémoires de JASS ( mapping Warcraft ) mais ceux de JavaScript tu m'apprends quelque chose ...
Enfin de mon point de vue si IE leak de la mémoire c'est aux développeurs de chez Microsoft de régler le problème.

Cependant comme ta fonction m'a l'air pas mal c'est possible que je l'utilise un jour.
Je crois y voir une petite erreur par contre :
obj.detachEvent("onclick",ieHandler);

devrait plutôt être :
obj.detachEvent("on"+type,ieHandler);

non ?
a écrit :
Je crois y voir une petite erreur par contre :


Bien vu Changaco!

a écrit :
l'idée est très bonne, mais je serais plus partisan d'utiliser des fonctions codées par dean edwards ou crockford


Le problème c'est que ces fonctions ne permette pas de passer ce fameux parametre optionnel. Par contre j'ai bien aimé le JScript Memory Leaks de Douglas Crockford, même si je n'ai pas tout compris, aurais tu un exemple de comment mettre en place ce genre d'outil? Cela semble régler non seulement les problème de mémoire liés aux événements mais aussi à d'autre éléments.

les addEvent qui me conviennent sont ceux de mootools ou de YUI, le problème c'est que pour un petit site avec trois scripts ça fait beaucoup de script juste pour profiter des fonctions events.

Ce que je voudrais c'est une fonction addEvent qui soit complète et robuste mais à la fois indépendante de ces grosse librairies.

J'ai vu que Firefox et d'autre navigateur peuvent aussi souffrir de fuites de mémoires, donc je me disait que cela pourrait être intéressant de rajouter un nettoyeur également pour les autres navigateurs :


function addEvent(obj, type, fn, par){
  var handlerEvent = function(event){
    if (!event) var event = window.event;   
    return fn.call(obj, event, par);
  };
  var cleanMemory = function(){      
    removeEvent(obj,type,handlerEvent);
    removeEvent(obj,'unload',cleanMemory);
    obj = null;
  };
  if(obj.addEventListener){
    if(type == 'mouseenter' || type == 'mouseleave'){
      handlerEvent = mouseNoBubble(fn, par);
      if(type == 'mouseenter')type='mouseover';
      if(type == 'mouseleave')type='mouseout';
    } 
    obj.addEventListener(type, handlerEvent, false );      
    window.addEventListener('unload',cleanMemory,false);  
  }else if(obj.attachEvent){
    obj.attachEvent("on"+type, handlerEvent);
    window.attachEvent("onunload",cleanMemory);     
  }       
}

function mouseNoBubble(fn, par){
  return function(e){
    if(this != e.relatedTarget){
      var t = this.getElementsByTagName('*');var l = t.length;     
      for(var i = 0;i < l; i++)
        if(e.relatedTarget == t[I])return;
    };
    fn.call(this, e, par);
  }
};

// By John Resig <http://ejohn.org/projects/flexible-javascript-events/>
function removeEvent( obj, type, fn ) {
  if(obj.detachEvent) {
    obj.detachEvent( 'on'+type, obj[type+fn] );
    obj[type+fn] = null;
  }else
    obj.removeEventListener( type, fn, false );
}


J'ai mis la fonction complète, j'utilise aussi un émulateur de onmouseenter et onmouseleave qui est très pratique.

C'est encore un peu du bricolage mais j'avance...
Modifié par matmat (01 Jul 2008 - 23:54)
Julien, pardon de te soliciter ainsi mais comme c'est toi qui a soulevé le problème je me permet Smiley langue , j'aimerais bien avoir plus de précision sur ces fuites de mémoire et ton avis sur ma solution.

En effet, je viens de faire pas mal de tests, que ce soit avec ie ou d'autres navigateurs et je n'ai pas vu de différences entre l'utilisation de la mémoire avec ou sans ces modifications.

J'ai vu également que Microsoft propose une mise a jour qui améliore le problème des fuites de mémoires de son navigateurs.

Donc je commençais à me demander si tout cela était vraiment utile.
Modifié par matmat (06 Jul 2008 - 00:41)
matmat a écrit :
En effet, je viens de faire pas mal de tests, que ce soit avec ie ou d'autres navigateurs et je n'ai pas vu de différences entre l'utilisation de la mémoire avec ou sans ces modifications.
Si tu veux voir une différence notable il faut que tu exécutes des milliers de fois l'instruction qui leak de la mémoire ... Il est en de même sous warcraft sauf que dans warcraft il est chose courante d'exécuter du code toutes les 0.01 secondes donc forcément la différence est vite notable ...
J'ai testé la fonction avec des scripts complexes plein d'événements différent types drag en drog, positions, etc.. et elle est complètement bugué!

Voilà la nouvelle, pour ceux que ça intéresse, je ne m'en sert pas encore, mais pour l'instant elle à passé tout les tests :


var eventListeners = [];

function addEvent(node, type, handler, par){

  if (!handler.$$guid) handler.$$guid = eventListeners.length+1;

  var handlerEvent = function(event){
    if (!event) var event = window.event;   
    return handler.call(node, event, par);
  };
  if(node.addEventListener){
    if(type == 'mouseenter' || type == 'mouseleave'){   
      handlerEvent = function(e){  
        if(this != e.relatedTarget){
          var t = this.getElementsByTagName('*');var l = t.length;     
          for(var j = 0;j < l; j++){
            if(e.relatedTarget == t[j])return;
          }
        };
        handler.call(this, e, par);
      };
      if(type == 'mouseenter')type='mouseover';
      if(type == 'mouseleave')type='mouseout';
    }
    node.addEventListener(type, handlerEvent, false);
  }else{
    node.attachEvent('on' + type, handlerEvent);
  }
  eventListeners.push({node: node, event: type, id: handler.$$guid, handler: handlerEvent});
}

function removeEventIndex(index){
  var ev = eventListeners[index];
  delete eventListeners[index];
  if(ev.node.removeEventListener) ev.node.removeEventListener(ev.event,ev.handler, false);  
  else ev.node.detachEvent('on' + ev.event,ev.handler);
}

function removeEvent(node, event, handler){
  for(i in eventListeners){
    if (eventListeners[j].id == handler.$$guid) removeEventIndex(j);
  }
  return null;
}

function cleanupEventListeners(){
  for (var j = eventListeners.length; j > 0; j--){
    if (eventListeners[j] != undefined) removeEventIndex(j);
  }
}

addEvent(window, 'unload', cleanupEventListeners);


Edit : Ajout d'un système d'identifiant pour chaque événement.
Modifié par matmat (16 Jul 2008 - 19:39)