11548 sujets

JavaScript, DOM et API Web HTML5

Bonjour,

J'essaye de comprendre le modèle d'évènements DOM 2, et j'ai plusieurs question sur ce sujet.

Les seules fois ou j'ai créé des scripts qui utilisent ce modèle je l'ai fait à l'aide de librairies adaptée. Sans l'aide de ces framworks j'ai systématiquement été confronté a des problèmes, donc je voudrais savoir si vous utilisez régulièrement cette fonction a la place du modèle DOM 1.

Le premier problème de taille que l'on rencontre c'est la perte du "this" sur la fonction callBack para internet explorer, cela peut être résolu par la fonction de John Resig http://ejohn.org/projects/flexible-javascript-events/. Soit...

L'autre problème c'est le passage d'un argument dans la fonction callback comme le this de la méthode en cours par exemple, comme résolu par Yahoo! UI Library http://developer.yahoo.com/yui/event/. Ok mais il faut utiliser Yahoo! UI Library donc pour un petit projet ça fait beaucoup pour un addEvent!

J'ai donc essayer de coder une fonction qui fasse la même chose et j'aimerais votre avis :

function addEvent(obj, type, fn, objParent){
  if(obj.addEventListener){
    obj.addEventListener(type, function(event){
      return fn.call(obj,event,objParent);
    }, false );
  }else if(obj.attachEvent){
    obj.attachEvent("on"+type, function(e){     
      if (!e) var e = window.event;
      return fn.call(obj, e, objParent);
    });
  }
}


La différence avec la fonction de yahoo! c'est que dans la this de la fonction callback on n' a pas accés aux propriétés de l'objet parent

dans yahoo on peut faire :
addListener(elementId,'click',function(event,par){
      alert(this.parameterProperty) 
    },parameter);


avec le code plus haut on fait :
addListener(elementId,'click',function(event,par){
      alert(par.parameterProperty) 
    },parameter);


J'ai essayer de le faire en ajoutant a la fonction :
if(obj.addEventListener){
        obj.addEventListener(type, function(event){
          for (var property in objParent){
            this[property] = objParent[property];       
		      }
          return fn.call(obj,event,objParent);
        }, false );
      }else if(obj.attachEvent){
        obj.attachEvent("on"+type, function(e){
    for (var property in objParent){
            this[property] = objParent[property];       
		      }
          if (!e) var e = window.event;
          return fn.call(obj, e, objParent);          
        });
      


Cela ne marche pas avec ie, mais tout bien réfléchi ce n'est pas un problème au contraire cela évitera la confusion entre les this des différentes fonctions et les éventuels paramètre identiques. Le principal est de pouvoir faire passer l'argument.
Modifié par matmat (18 Apr 2008 - 19:07)
Modérateur
Re, Smiley smile

Bon, je viens de passer rapidement sur ton message mais personnellement, je ne me sers (toujours) pas du this car j'initialise mes fonctions :
function addEvent() {
	var a = arguments;
	return document.addEventListener ?
		a[0].addEventListener(a[1], a[2], a[3] || false):
		a[0].attachEvent ?
			a[0].attachEvent('on' + a[1], a[2]):
			false;
}

function stopEvent(e) {
	if(e && e.stopPropagation && e.preventDefault) {
		e.stopPropagation();
		e.preventDefault();
	}
	else if(e && window.event) {
		window.event.cancelBubble = true;
		window.event.returnValue = false;
	}
	return false;
}

var [#blue]oA[/#] = document.getElementById('lien');

addEvent([#blue]oA[/#], 'click', monAlerte('Le nom de la balise est ', [#blue]oA[/#]));

function monAlerte(sMsg, [#blue]oA[/#]) {
	return function(e) {
		alert(sMsg + [#blue]oA[/#].nodeName.toLowerCase());
		return stopEvent(e);
	}
}
Au parsing du code, j'éxécute la fonction monAlerte en intégrant la référence sur mon lien au sein de la fonction anonyme qui, elle, n'est retournée qu'au moment du clic.
Modifié par koala64 (18 Apr 2008 - 20:01)
Changaco a écrit :
Bonjour.
Édit : et plus précisément ce message.


C'est exactement la même chose que la fonction de John Resig... sauf qu'elle n'a pas de paramètre optionnel.

koala64 a écrit :

Au parsing du code, j'éxécute la fonction monAlerte en intégrant la référence sur mon lien au sein de la fonction anonyme qui, elle, n'est retournée qu'au moment du clic.


Imaginons un cas un peu plus compliqué ou tu as besoin d'exécuter une autre fonction dépendante d'une autre.

function constructSomething{

  var fnc = this;
  
  this.init = function(){
  
    var elementId = document.getElementById('lien');

    addEvent(elementId,'click',function(event){
      fnc.nextStaff(elementId);    
    });

  };
  
  this.nextStaff = function(h){

    alert(elementId.nodeName.toLowerCase());

  };

};


Tu copie dans ce cas le this dans une variable? est ce que ça ne dédouble pas l'objet inutilement?
Modérateur
Hello, Smiley smile

Ben je m'y serais pris comme suit :
addEvent(window, 'load', function() {
	var monObjet = new Classe;
	monObjet.init();
});

var Classe = function() {}, Cp = Classe.prototype = {

	init: function() {
		var oEl = document.getElementById('lien');
		addEvent(oEl,'click', Cp.nextStaff(oEl));
	},

	nextStaff: function(oEl) {
		var sTxt = 'Le nom du noeud est : ';
		var sMsg = sTxt + oEl.nodeName.toLowerCase();
		return function(e) {
			alert(sMsg);
			return stopEvent(e);
		}
	}

};
Je n'ai pas à copier l'objet ici et l'avantage, c'est que l'évaluation de sMsg s'est passée au chargement du script et non au moment du clic... ce qui donne un peu plus de répondant.
Salut!

vraiment super, c'est un manière vraiment simple est pratique de construire une classe.

Imaginons maintenant que notre classe est paramétrable avec plein d'options, il est un peu lourd, il me semble, d'initialiser la classe comme ça:


  var monObjet = new Classe;
  monObjet.setColor('#fff');
  monObjet.setSize('200px');
  monObjet.setDelay(500);
  monObjet.setRepeat(5);
  monObjet.initialize(5);


C'est un peu plus lisible et léger comme ça :

  var monObjet = new Classe;
  monObjet.initialize({
     color: '#000',
     size: '200px',
     delay: 500,
     repeat: 5
  });


Dans ce cas notre fonction serait :

var Classe = function(){}, Cp = Classe.prototype = {

  options:{
    color: '#fff',
    size: '100px',
    delay: 1000,
    repeat: 10
  },
 
  initialize: function(sendingOptions){

    Cp.options = setOptions(Cp.options,sendingOptions);

    $('start').addEvent('click',function(event){
      alert(Cp.options.color);    
    });
  }

};

ou la fonction setOptions() permet d'assigner les nouvelles options, si elle sont envoyées bien sur pour pouvoir aussi garder celle par défault

Une autre solution serait:

var monObjet = new Classe({color: '#000',size: '200px',delay: 500,repeat: 5});


qui s'écrirais comme ça :

var Classe = function(){
  return new Class(this,arguments)
}

var Cp = Classe.prototype = {

  options:{
    color:'#fff',
    size:'200px'
  },
  
  initialize: function(){
    $('start').addEvent('click',function(event){
      alert(Cp.options.size);    
    });
  }

};


ou la la classe Class est :

var Class = function(fn,args){
  if(fn.options){
    for (var I = 0; I < args.length; I++){
      for (var property in args[I]){
       fn.options[property] = args[I][property];       
      }
    }
  }
  return fn.initialize.apply(fn, args);
} 


C'est librement inspiré de la fonction Class de Mootools qui permet d'initialiser des classes de cette manière, elle est bien faite sauf que l'on est obligé de travailler avec les this, ce qui ne résout pas le problème initial. Le probléme du this est gérer dans Mootools avec la fonction bind() mais le reproduire compliquerais tellement la chose qu'il vaut mieux dans ce cas utiliser le framework.

Que penses tu des deux proposition si dessus pour pouvoir écrire des classes propres avec un système d'options, qu'elle méthode des deux (ou aucune) préfères tu?
Modifié par matmat (21 Apr 2008 - 00:57)
Salut!

Finalement je revient à ma solution initiale, en effet j'ai des problèmes d'écrasements avec les solutions sans this. Si je rappelle une deuxième fois ma classe, les options de ma première classe sont écrasées.

La solution proposé au début est la seule qui me permet de faire :


addEvent(el,'click',function(event,fncParent){
   alert(this.href);
   alert(fncParent.options.color);
   fncParent.nextStaff();
},this);


Et ce à chaque fois que je rappelle ma classe avec de nouvelles options.

A noter que l'on peut aussi utliser le prototype bind :


Function.prototype.bind = function (obj) {
  var fn = this;
  return function(){
    return fn.apply(obj,arguments);
  };
};


Comme ça :


addEvent(el,'click',function(event,fncParent){
   alert(this.options.color);
   this.nextStaff();
}.bind(this));


C'est très pratique si on n'a pas besoin du this sur l'élément. C'est aussi très pratique sur la fonction forEach par exemple.