Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox



Apprendre a créer des pages dynamiques en javascript grâce à actionscript

Compatible JavaScript. Cliquer pour en savoir plus sur les compatibilités.Compatible ActionScript. Cliquer pour en savoir plus sur les compatibilités.Par pierre1405 (Pierre CORBEL), le 23 mai 2007

En action script, il est courant d'utiliser une classe pour générer le contenu d'un movie clip sans avoir à utiliser l'API de flash. De la même façon, en javascript, on peut générer le contenu d'une page sans utiliser d'HTML dans le body. Dans ce tutorial nous allons aborder 4 grandes parties :

  • comment associer une classe à un élément HTML.
  • comment générer dynamiquement de l'HTML grâce à l'objet Node.
  • Comment gérer dynamiquement les styles d'un élément HTML.
  • Comment gérer les évènements en Javascript.

Dans un précèdent tutorial, j'ai expliqué comment créer nos classes en Javascript, je vous conseille d'y jeter un coup d'oeil si vous n'avez jamais abordé les classes en Javascript.

L'exemple

Pour ce tutorial, nous allons créer un container simple. Ce container aura un header ainsi qu'un contenu qui pourra être affiché ou masqué en cliquant sur le header.

Javascript à la sauce AS3

Partons avec une version très simple de notre futur classe

//****************************************
//
// content : html affiché dans notre container
// titleHeader : texte affiche dans le header
// x : position en x du container
// y : position en y du container
// width : largeur du container
// height : hauteur du container
 
ResizableContainer = function ( content, titleHeader, x, y, height, width) {
	this.test = function () {
		alert ('It\'s a test');
	}
}

Classe heritant de MovieClip et DIV heritant de classe

Pour ce type de module, en AS3 ou AS2, on aurait pu créer une classe qui hérite de MovieClip.

public class ResizableContainer extends DisplayObject { .. }

Il aurai pu être intéressant de la même façon de faire hériter à ResizableContainer les proprietés et méthodes d'un div.

En théorie sur firefox, il aurait été possible d'utiliser le code suivant :

ResizableContainer = function ( x, y, heigth, width) {
HTMLDivElement.call (this, null);
}

Malheureusement HTMLDivElement est un objet natif et ce code génère une erreur… :(

il nous faudra donc faire l'inverse et faire hériter à nos instances de div notre classe ResizableContainer.

La seconde étape en AS3 serait d'attacher notre nouveau movieclip à la scène en utilisant addChild. En Javascript, on utilisera son équivalent insertBefore.

Donc en AS3, je ferai :

myWindow = new ResizableContainer ("titleHeader", "content", 20, 20, 200, 200);
_root.addChild (myWindow);

En Javascript, cela donnera :

 // création d'un div en cache
myWindow = document.createElement ("div");
 
// notre div hérite de ResizableContainer
ResizableContainer.call (myWindow, "content", "titleHeader", 100, 100, 200, 200, "titleHeader", "content");
 
// on attache notre container au body
document.body.insertBefore (myWindow, null);
 
myWindow.test (); // affiche "test"
  • myWindow = document.createElement (“div”);

Permet de créer en cache un DIV. createTextNode est aussi un fonction pratique, elle permet de générer un noeud de type text

  • document.body.insertBefore (myWindow, null);

Permet d'attacher le DIV créer au BODY. Dans mon exemple, j'attache mon container au body de ma page HTML. J'ai volontairement utilisé _root pour montrer la comparaison entre _root et document.body. On évitera l'utilisation de document.body pour exactement les mêmes raisons que _root, sinon, attention à vos genoux.

L'objet Node

la fonction insertBefore est une méthode de la classe Node dont hérite tous les HTMLelements. Cette classe est très puissante, elle peut vous permettre de composer tout votre body sans la moindre ligne d'HTML.

Créer un element HTML

Pour créer un noeud, vous pouvez les fonctions suivantes:

  • document.createElement (“htmltag”) pour créer un noeud HTML
  • document.createTextNode (“mon texte”) créer un noeud type text que vous pourrez attacher a un HTMLElement de type span par exemple

Il existe aussi des noeuds de type attributs, mais je ne les aborderai pas ici.

Manipuler l'architecture HTML

Pour manipuler les noeuds, voici en vrac quelques fonctions que j'aime utiliser :

  • nodeParent.insertBefore ( nodeChild, nextNode )

Deplace le noeud nodeChild dans nodeParent et le positionne avant le noeud nextNode.

  • nodeParent.removeChild ( nodeChild )

Retire nodeChild de nodeParent. Comme en AS3, l'instance nodeChild n'est plus affichée, mais elle reste conservée en cache. A moins d'être supprimée avec delete nodeChild, elle peut être ré-affichée ultérieurement.

  • data, insertData ( posFirstChar, “text” ), deleteData(posFirstChar, posLastChar)

Sert a manipuler des données de type texte.

  • parentNode, nextSibling.

Permet de se deplacer dans l'arborescence de vos noeuds. On appréciera notamment parentNode qui n'est pas sans rappeler la proprieté _parent d'actionscript. nextSibling permet d'avoir tous les enfants d'un element Html, while (nextNode = myNode.nextSibling) va nous permettre de parcourir tous les noeuds enfants de myNode. Plutot sympathique pour les tableaux et les listes.

La syntaxe monNoeud1.monNoeud2 est possible a condition de bien soigner son code. Le code suivant permet de reproduire cette hiérarchisation

monNoeud1 = document.createElement ("p");
monNoeud1.monNoeud2 = document.createTextNode ("un petit bout de texte")
monNoeud1.insertBefore (monNoeud1.monNoeud2, null);
alert (monNoeud1.monNoeud2.nodeValue); // display "un petit bout de texte"

En pratique

Modifions notre classe pour mettre tout cela en pratique

ResizableContainer = function ( content, titleHeader, x, y, height, width) {
 
	/*
	Ce code va reconstituer l'architecture HTML suivante
	  * this
	    * titleDiv
		  * texte du titre
		* HideShowDiv
		  * HideShowlink
		    * texte du link utiliser pour afficher/masquer le texte du container
		* contentDiv
			* texte de notre contenu
 
	*/
 
	// conteneur
	/*
	ce code va créer l'architecture suivante
	<ResizableContainer>
		<div>content</div>
	<ResizableContainer>
	*/
	this.contentDiv = document.createElement ("div");
	this.contentDiv.innerHTML = content;
	this.insertBefore (this.contentDiv, null);
 
	// titre du conteneur 
	/*
	ce code va créer l'architecture suivante
	<ResizableContainer>
		<div>title</div>
		<div>content</div>
	<ResizableContainer>
	*/
	this.titleDiv = document.createElement ("div");
	this.titleDiv.insertBefore (document.createTextNode (titleHeader), null);
	this.insertBefore (this.titleDiv, this.contentDiv);
 
	// bouton afficher/masquer
	/*
	<ResizableContainer>
		<div>title</div>
		<div><a>masquer</a></div>
		<div>content</div>
	<ResizableContainer>
	*/
	this.HideShowDiv = document.createElement ("div");
	this.HideShowDiv.HideShowlink = document.createElement ("a");
	this.HideShowDiv.HideShowlink.insertBefore (document.createTextNode ("hide"), null);
	this.HideShowDiv.insertBefore (this.HideShowDiv.HideShowlink, null);
	this.insertBefore (this.HideShowDiv, this.contentDiv);
}

Une fois la classe modifiée, le constructeur va generer le code HTML suivant

<ResizableContainer>
	<div>title</div>
	<div><a>masquer</a></div>
	<div>content</div>
<ResizableContainer>

Pour plus d'infos vous pouvez jeter un coup d'oeil a cette documentation

Modification des styles

Vous avez deux méthodes pour modifier les styles d'un noeud HTML créé dynamiquement.

Via une feuille de style

Vous pouvez attribuer en javascript une id ou une classe à un élément HTML via les propriétés id et className. Vous pouvez ensuite créer une feuille de style pour affecter des styles à vos classes et id.

Pour avoir des id uniques, il peut être pratique de créer une variable statique qui s'incrémente chaque fois que l'on crée une nouvelle instance ( dans ma classe, il s'agit de ResizableContainer.id ). Les id uniques sont très utiles pour les tests.

en modifiant les styles via Javascript

Tout vos noeuds HTML ont une propriété style très simple à utiliser. Cette propriété permet d'avoir accès à tous les styles de votre élément HTML et de les surdéfinir.

Par exemple, pour masque un noeud HTML, je peux utiliser :

monNoeud.style.display = “none”;

Je n'ai encore jamais tester, mais il me semble que l'on peut aussi générer et modifier dynamiquement des feuilles de styles.

En pratique

Si j'integre a ma page les styles suivants

...
}
 
.ResizableContainer .title {
...
}
 
.ResizableContainer .hideShow {
...
}
 
.ResizableContainer .hideShow a {
...
}
 
.ResizableContainer .content {
...
}

Et que j'ajoute dans mon constructeur le code suivant

 
ResizableContainer = function ( content, titleHeader, x, y, width, height ) {
	...
 
	// cette variable statique permet d'avoir des ID uniques pour chaques 
	// instances pour des personnalisations plus fines
	ResizableContainer.id++;
 
	// Je fixe des ID et des class afin de pouvoir manipuler le design 
	// via une feuille de style externes.
	this.instanceId = ResizableContainer.id++;
	this.id = "ResizableContainer"+this.instanceId;
	this.titleDiv.id = "title"+this.instanceId;
	this.HideShowDiv.id = "hideShow"+this.instanceId;
	this.contentDiv.id = "content"+this.instanceId;
 
 
	this.className = "ResizableContainer";
	this.titleDiv.className = "title";
	this.HideShowDiv.className = "hideShow";
	this.contentDiv.className = "content";
 
	// A partir d'ici on doit utiliser javascript assigner les styles.
	// position et taille de notre container
	this.style.position = "absolute";
	this.style.left = x+"px";
	this.style.top = y+"px";
	this.style.height = height+"px";
	this.height = height;
	this.style.width = width+"px";
 
}
// cette variable statique permet d'avoir des ID uniques pour chaques 
// instances
ResizableContainer.id = 1;

Je vais obtenir ce resultat

 Avant et après

Gestion d'evenements

En Javascript, 2 modèles sont en concurrence :

  • myHTMLElement.onClick = function () {…}

Comme en AS1, ce modèle est simple à utiliser, mais il ne permet pas d'ajouter plus d'un listener. this fais référence a myHTMLElement.

  • myHTMLElement.addEventListener ('onclick', monObjet.mafonction, true ); pour firefox et myHTMLElement.attachEvent ('onclick', monObjet.mafonction ); pour IE

Avec cette méthode, vous pouvez ajouter autant de listener que vous le souhaitez et les supprimer avec removeListener (firefox) ou detachEvent (IE). Le gros problème est que pour IE, la fonction monObjet.mafonction est dupliquée et associée a l'objet window. Donc pour IE, this fait référence a window. Il n'y a pas de problèmes pour firefox, this fais bien référence a monObjet.

Afin de conserver les avantages de la programmation objet et d'avoir un comportement similaire sous IE et firefox, j'utilise personnellement le premier modèle. Mais le second modèle sous Firefox est très intéressant.

Ajoutons un evenement sur le bouton show/hide

Il ne nous reste plus qu'a gerer les actions sur notre bouton hide/show pour terminer notre classe.

ResizableContainer = function ( content, titleHeader, x, y, width, height ) {
 
	...
 
	// Assigne une fonction a l'evenement onclick pour 
	// l'element this.HideShowDiv.HideShowlink
	this.HideShowDiv.HideShowlink.onclick = function () {
		// this faire reference a HideShowlink
		// donc this.parentNode.parentNode fait reference a notre container
		var container = this.parentNode.parentNode;
		container.hideShow ();
	}
 
	this.hideShow = function () {
		alert ("hide/show container");
	}
 
}

this.HideShowDiv.HideShowlink.onclick = function () { … } permet d'ajouter notre evenement click au lien de notre header et var container = this.parentNode.parentNode; nous permet de recuperer un reference vers notre container.

Finissons notre classe

J'ai rajouter le code nécessaire dans this.hideShow () pour ouvrir/fermer notre container. Rien de bien complique ici, lorsque je clique sur le lien du bouton afficher masquer, j'affiche ou je masque le contenu et je change le texte du bouton.

À noter cependant:

  • this.style.height = ””; qui reinitialise height à sa valeur par defaut.
  • this.HideShowDiv.HideShowlink.firstChild.nodeValue = “show”; nodeValue permet de modifier un noeud texte.
	this.hideShow = function () {
		if (!this.isOpen) {
			this.contentDiv.style.display = "";
			this.style.height = height+"px";
			this.isOpen = true;
			this.HideShowDiv.HideShowlink.firstChild.nodeValue = "hide";
		}
		else {
			this.contentDiv.style.display = "none";
			this.style.height = "";
			this.isOpen = false;
			this.HideShowDiv.HideShowlink.firstChild.nodeValue = "show";
		}
	}

La source complete

//****************************************
//
// content : html affiche dans notre container
// titleHeader : texte affiche dans le header
// x : position en x du container
// y : position en y du container
// width : largeur du container
// height : hauteur du container
 
ResizableContainer = function ( content, titleHeader, x, y, width, height ) {
 
	/*
	Ce code va reconstituer l'architecture HTML suivante
	  * this
	    * titleDiv
		  * texte du titre
		* HideShowDiv
		  * HideShowlink
		    * texte du link utiliser pour afficher/masquer le texte du container
		* contentDiv
			* texte de notre contenu
 
	*/
 
	// conteneur
	/*
	ce code va creer l'architecture suivante
	<ResizableContainer>
		<div>content</div>
	<ResizableContainer>
	*/
	this.contentDiv = document.createElement ("div");
	this.contentDiv.innerHTML = content;
	this.insertBefore (this.contentDiv, null);
 
	// titre du conteneur 
	/*
	ce code va creer l'architecture suivante
	<ResizableContainer>
		<div>title</div>
		<div>content</div>
	<ResizableContainer>
	*/
	this.titleDiv = document.createElement ("div");
	this.titleDiv.insertBefore (document.createTextNode (titleHeader), null);
	this.insertBefore (this.titleDiv, this.contentDiv);
 
	// bouton afficher/masquer
	/*
	<ResizableContainer>
		<div>title</div>
		<div><a>masquer</a></div>
		<div>content</div>
	<ResizableContainer>
	*/
	this.HideShowDiv = document.createElement ("div");
	this.HideShowDiv.HideShowlink = document.createElement ("a");
	this.HideShowDiv.HideShowlink.insertBefore (document.createTextNode ("hide"), null);
	this.HideShowDiv.insertBefore (this.HideShowDiv.HideShowlink, null);
	this.insertBefore (this.HideShowDiv, this.contentDiv);
 
	// cette variable statique permet d'avoir des ID uniques pour chaques 
	// instances pour des personnalisations plus fines
	ResizableContainer.id++;
 
	// Je fixe des ID et des class afin de pouvoir manipuler le design 
	// via une feuille de style externes.
	this.instanceId = ResizableContainer.id++;
	this.id = "ResizableContainer"+this.instanceId;
	this.titleDiv.id = "title"+this.instanceId;
	this.HideShowDiv.id = "hideShow"+this.instanceId;
	this.contentDiv.id = "content"+this.instanceId;
 
 
	this.className = "ResizableContainer";
	this.titleDiv.className = "title";
	this.HideShowDiv.className = "hideShow";
	this.contentDiv.className = "content";
 
	// A partir d'ici on doit utiliser javascript assigner les styles.
	// position et taille de notre container
	this.style.position = "absolute";
	this.style.left = x+"px";
	this.style.top = y+"px";
	this.style.height = height+"px";
	this.height = height;
	this.style.width = width+"px";
 
	// Assigne une fonction a l'evenement onclick pour 
	// l'element this.HideShowDiv.HideShowlink
	this.HideShowDiv.HideShowlink.onclick = function () {
		// this faire reference a HideShowlink
		// donc this.parentNode.parentNode fait reference a notre container
		var container = this.parentNode.parentNode;
		container.hideShow ();
	}
	// Rien de bien complique dans cette fonction qui affiche/masque
	// le contenu de container
	// A noter cependant, monElementHTML.style.maPropieteCSS = "", 
	// reinitialise maPropieteCSS a sa valeur par defaut et nodeValue qui
	// permet de modifier un noeud de type texte
	this.hideShow = function () {
		if (!this.isOpen) {
			this.contentDiv.style.display = "";
			this.style.height = height+"px";
			this.isOpen = true;
			this.HideShowDiv.HideShowlink.firstChild.nodeValue = "hide";
		}
		else {
			this.contentDiv.style.display = "none";
			this.style.height = "";
			this.isOpen = false;
			this.HideShowDiv.HideShowlink.firstChild.nodeValue = "show";
		}
	}
 
	// ces variables seront utilisees pour savoir si le contenu est affiche ou non
	// et pour redimensionner le container a la bonne hauteur
	this.isOpen = true;
	this.openHeight = height;	
}
// cette variable statique permet d'avoir des ID uniques pour chaques 
// instances
ResizableContainer.id = 1;
 
// creation d'un div en cache
myWindow = document.createElement ("div");
// notre div herite de ResizableContainer
ResizableContainer.call (myWindow, "content", "titleHeader", 100, 100, 200, 200, "titleHeader", "content");
// on attache notre container au body
document.body.insertBefore (myWindow, null);

En savoir plus