11484 sujets

JavaScript, DOM et API Web HTML5

Bonjour à tous,

Une question récurrente que je me pose lorsque je développe des applis en javascript : j'ai des objets javascript qui créent des composants html.
J'aimerais que certains évenements sur ces composants html executent des méthodes de mon objet...

Bref, un exemple vaut mieux qu'une explication longue et indigeste :

Mon objet JavaScript, Button, permettant d'afficher un bouton (whaouuu !) :


var Button = Class.create();
Button.prototype={
   initialize: function(){
   },
   displayButton: function(){
      var button = document.createElement("button");
      button.setAttribute("type","button");
      button.setAttribute("value","Clique moi!");
      document.getElementsByTagName("body")[0].appendChild(button);
   },
   sayButtonMessage: function(msg){
      alert(msg);
   }
}


Maintenant, mon fichier html, dans lequel je créé et affiche mon bouton :


<html>
[...]
<body>
<script type="text/javascript">
var button = new Button();
button.displayButton();
</script>
</body>
</html>


Tout ceci devrait m'afficher un très beau bouton.
OK. Mon problème est donc que sur l'évenement "onclick" de ce bouton, je souhaite executer la methode sayButtonMessage de cet objet...

Mais la, impossible d'ajouter à la méthode displayButton la ligne suivante :


      button.setAttribute("onclick",this.sayButtonMessage("Hello World!"));


ceci serait très bête et surtout inutile...

Comment feriez-vous pour résoudre ce problème ?
J'ai une solution mais pas satisfaisante qui serait d'utiliser une fonction statique...

Bref, merci d'avance pour vos idées !

(Note: pour info, je tiens à préciser que l'exemple de code cité ci-dessus est débile et tout à fait inutile, je l'ai juste écrit pour expliquer ma question !)
Modifié par Antoine_fr (25 Dec 2006 - 12:16)
Modérateur
Salut,

oui, ainsi que celui d'Eldebaran, ce qui te permettra de comprendre ce code :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
	<head>
		<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
		<title>Exemple</title>

<script type="text/javascript">//<![CDATA[

(function(){

var oO =
{
	'balise': 'button',
	'txt': 'Clique moi !',
	'msg': 'Hello World !',

	_Connect: function(oElem, sEvType, fn, bCapture)
	{
		oElem.addEventListener ?
			oElem.addEventListener(sEvType, fn, bCapture):
			oElem.attachEvent ?
				oElem.attachEvent('on' + sEvType, fn):
				false; //oElem['on' + sEvType] = fn; si compatibilité avec "très" vieux navigateurs
	},

	_Display: function(elem, txt, msg)
	{
		var oElem = document.createElement(elem),
			oTxt = document.createTextNode(txt);
		oElem.appendChild(oTxt);
		document.body.appendChild(oElem);
		oO._Connect(oElem, 'click', function() { oO._sayMessage(msg); }, false);
	},

	_sayMessage: function(msg)
	{
		alert(msg);
	}
};
try { oO._Connect(window, 'load', function() { oO._Display(oO['balise'], oO['txt'], oO['msg']); }, false); } catch(e) {};

})();

//]]></script></head><body></body></html>

Modifié par koala64 (22 Dec 2006 - 16:50)
@FlorentG : Merci, c'est vrai que je n'avais pas lu le tuto Smiley confused

@koala64 : Merci, c'est ce que je cherchais, je teste ça dès que possible !

Smiley biggrin super la rapidité et la qualité des réponses !
L'exemple que tu décris koala64, ainsi que ce qu'explique (très bien) Eldebaran sur son tuto, s'applique parfaitement dans le cas d'un litteral objet.

Mais moi je tentais d'écrire une classe (je connais beaucoup plus Java, et le modèle objet est très... différent de JS ! ce que je dis est donc peut être une abbération).

Une classe donc, dont je puisse créer plusieurs instances, avec chacune un listener d'évenement sur des évenements particuliers...

Je vous joint ma classe de test, utilisant la méthode décrite dans les tutos pour ajouter un listener d'événement.


var Grille = Class.create();
Grille.prototype={
	initialize: function(l,h){
		this.largeur=l;
		this.hauteur=h;
		this.grille=new Array();
		for(a=0;a<this.largeur;a++){
			this.grille[a]=new Array();
			for(j=0;j<this.hauteur;j++){
				this.grille[a][j]=false;
			}
		}
	},
	
	display: function(){
		var table = document.createElement("table");
		table.setAttribute("class","grille");
		for(a=0;a<this.hauteur;a++){
			var tr = document.createElement("tr");
			for(j=0;j<this.largeur;j++){
				var td = document.createElement("td");
				var className="cellNotActive";
				if(this.grille[j][a]==true)
					className="cellActive";
				td.setAttribute("class",className);
				this._Connect(td, 'click', function() { this.inverserCellule(a,j); }, false);
				tr.appendChild(td);
			}
			table.appendChild(tr);
		}
		document.getElementsByTagName("body")[0].appendChild(table);
	},
	
	isActiveCellule: function(x,y){
		var isActive=false;
		if(x>=0 && x<this.largeur && y>= 0 && y<this.hauteur)
			isActive=this.grille[x][y];
		return isActive;
	},
	
	setCellule: function(x,y,active){
		if(x>=0 && x<this.largeur && y>=0 && y<this.hauteur){
			this.grille[x][y]=active;
		}
	},
	
	inverserCellule: function(x,y){
		var active = !this.isActiveCellule(x,y);
		this.setCellule(x,y,active);
	},
	
	_Connect: function(oElem, sEvType, fn, bCapture){
		oElem.addEventListener ? oElem.addEventListener(sEvType, fn, bCapture): oElem.attachEvent ? oElem.attachEvent('on' + sEvType, fn):false;
	}
}


Cette classe permet de créer un tableau, dans lequel on peut cliquer sur des cellules, ce qui a pour effet de changer la classe de la cellule et donc sa couleur (via la css).

Ca ne fonctionne pas car la "fonction" (méthode !) appelée lors d'un clic sur une cellule est inexistante... en fait c'est le this qui dans ce contexte n'existe pas, et pourtant c'est exactement ce que je veux faire...

Message d'erreur (logique) lors d'un clic sur une cellule :

"this.inverserCellule is not a function"

Merci pour vos suggestions...!
Ok, donc une solution que j'ai trouver pour appeler une méthode de mon objet sur l'évenement souhaité consite à modifier ma méthode display de la façon suivante, juste avant de connecter l'évenement click :


var laGrille=this;
this._Connect(td, 'click', function() { onClickOnCellule(laGrille,i,j); }, false);


puis de définir une fonction globale comme ceci :


function onClickOnCellule(grille,x,y){
	grille.inverserCellule(x,y);
}


de cette façon je peux appeler une méthode de mon objet Grille lors d'un clic sur une cellule du tableau...

Bon, donc ma conclusion, c'est que ce n'est pas très "propre", et que la gestion des évenements par un objet n'est pas très simple à mettre en place... mais ça conserve une certaine logique !

La où il n'y a plus de logique, c'est que sur n'importe quelle cellule que je clique, la fonction onClickOnCellule est appelée... avec les mêmes valeurs pour x et y !!!
AMHA, je pense que ceci est dû à la ********* de portée des variables en JavaScript...
Modérateur
Salut,

Je n'ai pas trop compris ce que tu veux faire mais bon, si ça peut te donner des idées...
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
	<head>
		<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
		<title>Exemple</title>

<style type="text/css">/*<![CDATA[*/
table
{
	background-color: #58F;
	width: 400px;
	height: 400px;
}
table td
{
	border: 1px solid #F85;
}
table td:hover
{
	background-color: #8F5;
	cursor: pointer;
}
/*]]>*/</style>
<script type="text/javascript">//<![CDATA[

(function(){


var MyClass =
{
	_create: function()
	{
		return function()
		{
			this._initialize.apply(this, arguments);
		}
	}
};

var Grille = MyClass._create();

Grille.prototype =
{
	_initialize: function(l, h)
	{
		this.largeur = l;
		this.hauteur = h;
		this.grille = new Array();
		var a = this.hauteur;
		for(a; a > 0; a--)
		{
			this.grille[a] = new Array();
			var j = this.largeur;
			for(j; j > 0; j--)
			{
				this.grille[a][j] = false;
			}
		}
	},

	_getTarget: function(e)
	{
		return e.target || e.srcElement;
	},

	_display: function()
	{
		var table = document.createElement("table");
		table.setAttribute("class", "grille");
		var h = this.hauteur;
		for(h; h > 0; h--)
		{
			var tr = document.createElement("tr");
			var l = this.largeur;
			for(l; l > 0; l--)
			{
				var td = document.createElement("td");
				var className = "cellNotActive";
				if(this.grille[l][h] == true) className="cellActive";
				td.setAttribute("class", className);
				td.id = 'cell' + (this.largeur + 1 - l) + (this.hauteur + 1 - h);
				var oTarget = this._getTarget;
				this._connect(td, 'click', function(e)
				{
					var oTd = this.prototype = Grille.prototype;
					oTd._inverserCellule(oTarget(e));
				}, false);
				tr.appendChild(td);
			}
			table.appendChild(tr);
		}
		document.body.appendChild(table);
	},

	_isActiveCellule: function(oTarget)
	{
		var isActive = false, l = oTarget.id.substring(4,5), h = oTarget.id.substring(5);
		if(l >= 0 && l < this.largeur && h >= 0 && h < this.hauteur)
			isActive = this.grille[l][h];
		return isActive;
	},

	_setCellule: function(active, oTarget)
	{
		var l = oTarget.id.substring(4, 5), h = oTarget.id.substring(5);
		alert('l = ' + l + ' ; h = ' + h);
		if(l >= 0 && l < this.largeur && h >= 0 && h < this.hauteur)
		{
			this.grille[l][h] = active;
		}
	},

	_inverserCellule: function(oTarget)
	{
		var active = !this._isActiveCellule(oTarget);
		this._setCellule(active, oTarget);
	},

	_connect: function(oElem, sEvType, fn, bCapture)
	{
		oElem.addEventListener ?
			oElem.addEventListener(sEvType, fn, bCapture):
			oElem.attachEvent ?
				oElem.attachEvent('on' + sEvType, fn):
				false;
	}
}

var oGrille = new Grille(9, 9);
oGrille._connect(window, 'load', function() { oGrille._display(); }, false);

})();

//]]></script></head><body></body></html>
C'est chouette, avec ce script, je peux faire un toucher-couler mais à part ça... ??? Smiley sweatdrop