<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapitre SYSTEM "../ressources/chapitre22.dtd">

<chapitre typecourssiteweb="dhtml">
	<cours nomfichier="bonnespratiques">Cours de manipulation du DOM et DHTML</cours>
	<entete>
		<titre>Bonnes pratiques de codage en javascript</titre>
		<auteur email="Gilles.Chagnon@upmc.fr">G. Chagnon</auteur>
		<resume>La programmation en javascript atteint des limites quand des codes issus de fichiers source différents interfèrent. Il existe des méthodes de codage qui minimisent ces risques.</resume>
		<motsclefs>dhtml, dom, javascript, evenement, gestion, event, event handler, bonnes pratiques</motsclefs>
	</entete>
	<corpus>
		<partie titre="Introduction" ancre="intro">
			<paragraphe>
				<texte>Il est courant de rencontrer sur Internet des codes <code type="langage">JavaScript</code> écrits par d'autres programmeurs, et que l'on souhaite incorporer à ses pages. Il arrive aussi que l'on souhaite réutiliser du contenu, des fonctions que l'on a soi-même écrites précédemment.</texte>
				<texte>Cependant, ce désir de vouloir reprendre du code de sources externes se heurte souvent à des difficultés&#160;:</texte>
				<liste>
					<item><texte>le code est incompatible avec certains navigateurs&#160;;</texte></item>
					<item><texte>les noms des fonctions entrent en compétition. Par exemple, il est courant d'appeler au chargement de la page une fonction que l'on nomme <code>Init()</code>&#160;; mais ce nom est très commun et si l'on souhaite mélanger des codes issus de plusieurs sources, il est probable que des erreurs soient créées&#160;;</texte></item>
					<item><texte>les variables elles-mêmes peuvent porter des noms ambigus. Cela est plus grave, dans la mesure où la redéfinition d'une variable n'est pas signalée comme un bogue du code, et peut donc passer inaperçue&#160;;</texte></item>
					<item><texte>les gestionnaires d'événement peuvent s'écraser l'un l'autre.</texte></item>
				</liste>
			</paragraphe>
			<paragraphe>
				<texte>Heureusement, de «&#160;bonnes pratiques&#160;» de codage permettent de ne pas tomber dans ces pièges. On pourra consulter avec profit le blog de Christian Heilmann, intitulé <reference href="http://www.wait-till-i.com/" lang="en">Wait-till-I-come</reference>.</texte>
			</paragraphe>
		</partie>
		<partie titre="Généralités" ancre="généralités">
			<section titre="Ajout de contenu" ancre="ajoutcontenu">
				<paragraphe>
					<texte>Nous avons vu dans un chapitre précédent comment <reference href="modifs.html#ajout">ajouter du contenu à un document</reference>. Cependant, cette possibilité présente de gros inconvénients en termes d'accessibilité&#160;: les utilisateurs ne disposant pas de <code type="langage">JavaScript</code> sont dans l'incapacité de consulter ce contenu si des précautions ne sont pas prises.</texte>
					<texte>À cet égard, il est primordial que <valeur>tout contenu informatif présent dans un document doit être présent dans le code HTML de la page</valeur>. Par exemple, il ne faut pas utiliser <code type="langage">JavaScript</code> pour ajouter, par exemple, un menu de navigation.</texte>
					<texte>Une alternative consiste à faire en sorte que le contenu soit déjà présent dans la page, puis, au chargement du document, de le cacher par <code type="langage">JavaScript</code> pour ensuite le faire réapparaître en fonction des besoins.</texte>
				</paragraphe>
				<exercice titre="Masquage d'éléments au chargement d'une page" ancre="exoMasquage">
					<enonce href="exercices/exo21.html"/>
					<correction href="exercices/exo21cor.html"/>
				</exercice>
			</section>
			<section titre="La mise en forme doit être effectuée à l'aide de CSS" ancre="jsetcss">
				<paragraphe titre="Préférer className" ancre="classname">
					<texte><code type="langage">CSS</code> est un langage qui est dédié à la mise en forme. Par conséquent, il est toujours plus rapide de changer l'apparence d'un élément en faisant appel à CSS qu'à <code type="langage">JavaScript</code>. Cela a pour conséquence de préférer systématiquement l'utilisation de la propriété <code>className</code> aux propriétés de l'objet <code>style</code>. Par exemple, si l'on doit changer en rouge la couleur d'un élément <code>elt</code>, on définira de préférence une classe <code>"exemple"</code> qui spécifiera une couleur de police rouge et on fera <code>elt.className="exemple";</code> au lieu d'indiquer <code>elt.style.color="red"</code>. Cela offre en outre l'avantage d'externaliser en-dehors du script la définition de cette mise en forme.</texte>
				</paragraphe>
				<paragraphe titre="Ne pas écraser d'informations déjà présentes" ancre="nePasÉcraser">
					<texte>Certains éléments sont déjà pourvus d'une classe dans le code HTML. Manipuler la propriété <code>className</code> peut faire perdre une mise en forme. Rappelons en effet qu'il est tout à fait possible pour un élément d'avoir plusieurs classes&#160;: <code><![CDATA[<span id="span1" class="classe1 classe2 classe3">(...)</span>]]></code>. Si on utilise directement <code>span1.className="classe3"</code>, par exemple, l'élément va perdre toutes ses autres classes, ce qui n'est pas forcément le but recherché. Afin de l'éviter, il faut tester au préalable si <code>className</code> existe pour l'élément en question, puis <valeur>ajouter</valeur> la nouvelle classe à son contenu si c'est le cas.</texte>
				</paragraphe>
				<exercice titre="Ajout d'une classe" ancre="exoAjoutClasse">
					<enonce href="exercices/exo24.html"/>
					<correction href="exercices/exo24cor.html"/>
				</exercice>
			</section>
			<section titre="Conditionner l'exécution d'un code" ancre="conditionnement">
				<paragraphe titre="Tester la disponibilité d'une méthode ou d'une propriété" ancre="testDispo">
					<texte>Avant de faire appel à une propriété ou une méthode, il vaut mieux au prélable s'assurer qu'aucune erreur ne sera produite si elle n'est pas disponible. Il était d'usage de faire une détection du navigateur utilisé, mais cette pratique a disparu pour laisser place à des méthodes plus efficaces.</texte>
					<texte>Il suffit en effet de tester si ce dont on a besoin ne produit pas de code erreur, et de conditionner la poursuite de l'exécution du code au fait qu'il n'y a pas d'erreur.</texte>
					<texte>Supposons par exemple que l'on souhaite utiliser la méthode <code>getElementById</code>. On écrira...</texte>
					<exemplejavascript>
						<instruction name="if"/><autres> (!</autres><propriete name="document."/><autres>getElementById) </autres><instruction name="return"/><finligne/><sautligne/>
						<bloc>
							<autres>/* Code à exécuter quand getElementById est supporté. */</autres><sautligne/>
						</bloc>
					</exemplejavascript>
					<texte>Ainsi, le navigateur ignorera «&#160;naturellement&#160;» le code qu'il ne peut pas comprendre.</texte>
				</paragraphe>
				<paragraphe titre="Tester la présence d'un élément" ancre="dispoÉlément">
					<texte>Avant d'agir sur un élément, encore faut-il être sûr qu'il existe... Cela peut paraître superflu quand on n'intervient que sur une seule page, mais lorsque le fichier <code type="typefichier">JavaScript</code> est amené à être mis en commun pour plusieurs documents, il est possible que le cas apparaisse où un élément n'existe pas sur une page... ce qui peut amener des erreurs.</texte>
					<texte>Selon le cas, on peut alors soit placer les instructions réservées à un élément à l'intérieur d'un test, soit stopper l'exécution du script&#160;:</texte>
					<exemplejavascript>
						<instruction name="if"/><autres> (</autres><propriete name="document."/><fonction name="getElementById"><variable name='"id1"'/></fonction>
						<bloc>
							<autres>/* Code à exécuter quand l'élément d'identifiant id1 existe. */</autres><sautligne/>
						</bloc>
					</exemplejavascript>
					<exemplejavascript>
						<instruction name="if"/><autres> (!</autres><propriete name="document."/><fonction name="getElementById"><variable name='"id1"'/></fonction><autres> </autres><instruction name="return"/><finligne/><sautligne/>
						<bloc>
							<autres>/* Code à exécuter quand l'élément d'identifiant id1 existe. */</autres><sautligne/>
						</bloc>
					</exemplejavascript>
				</paragraphe>
				<exercice titre="Tester la disponibilité d'une méthode" ancre="exoTestDispo">
					<enonce href="exercices/exo22.html"/>
					<correction href="exercices/exo22cor.html"/>
				</exercice>
				<exercice titre="Tester la présence d'un élément" ancre="exoTestÉlément">
					<enonce href="exercices/exo23.html"/>
					<correction href="exercices/exo23cor.html"/>
				</exercice>
			</section>
		</partie>
		<partie titre="Lever les ambiguïtés des variables" ancre="variablesAmbi">
			<section titre="Introduction" ancre="introAmbi">
				<paragraphe>
					<texte>Il arrive que des variables soient redéfinies entre plusieurs scripts épars dans des fichiers distincts. Une redéfinition malencontreuse d'une variable peut perturber le fonctionnement d'un script, et il faut donc au maximum limiter le risque d'interférences entre deux variables. <valeur>La toute première précaution à prendre est donc de limiter au maximum la portée d'une variable, en recourant à la déclaration <code>var</code></valeur>.</texte>
				</paragraphe>
				<paragraphe>
					<texte>On pourrait penser à utiliser des noms de variables très explicites, voire à les préfixer ou les suffixer, comme par exemple <code>monCodeAMoiRienQuAMoi_maVariable</code>, mais il faut pour cela trouver un préfixe unique (celui de l'exemple précédent a certes peu de chances d'être repris&#160;!), et surtout, par la suite, se garder des fautes de frappe. Qui plus est, cela a comme inconvénient que si par extraordinaire, le préfixe est utilisé dans un autre fichier, l'ensemble des variables et fonctions doit être renommé. Ce n'est donc pas une solution très efficace.</texte>
				</paragraphe>
			</section>
			<section titre="Création et utilisation d'un objet" ancre="objet">
				<paragraphe titre="Création simple d'un objet" ancre="objetSimple">
					<texte><code type="langage">JavaScript</code> ne permet pas comme d'autres langages de définir des classes, et d'instancier ensuite des objets. Mais il est possible de créer directement des objets, grâce au mot-clef <code>Object</code>.</texte>
					<exemplejavascript>
						<declaration nomvariable="objet1" valeur="Object"/><finligne/>
						<sautligne/>
						<variable name="objet1.chaine"/><autres>="Bonjour!"</autres><finligne/>
						<variable name="objet1.affiche"/><autres>=</autres><fonction name="function"/><bloc><fonction name="alert"><variable name="this.chaine"/></fonction><finligne/></bloc>
						<sautligne/>
						<variable name="objet1.affiche()"/><finligne/>
					</exemplejavascript>
					<texte>La déclaration précédent définit l'objet <code>objet1</code>, lui attache une propriété (<code>chaine</code>) et une méthode que l'on appelle ensuite. Ainsi, la méthode est associée à cet objet uniquement, et il n'y a pas de risque que sa définition écrase une autre définition qui serait donnée pour un autre objet.</texte>
				</paragraphe>
				<paragraphe titre="Création d'un littéral objet" ancre="littéralObjet">
					<texte>Il est possible d'aller un peu plus loin dans la création d'un objet en utilisant un « littéral objet », mais cette méthode ne fonctionne pas pour les navigateurs anciens (<logiciel>Netscape</logiciel> jusqu'à la version&#160;3 et <logiciel>Internet Explorer</logiciel> jusqu'à la version&#160;4) La déclaration précédente s'écrit alors...</texte>
					<exemplejavascript>
						<instruction name="var"/><autres> </autres><variable name="objet1"/><autres> = </autres><sautligne/>
						<autres>{</autres><sautligne/>
						<variable name="chaine"/><autres>:"Bonjour!"</autres><autres>,</autres><sautligne/>
						<variable name="affiche"/><autres>:</autres><fonction name="function"/><bloc><fonction name="alert"><variable name="this.chaine"/></fonction><finligne/></bloc>
						<sautligne/>
						<autres>}</autres>
						<sautligne/>
						<variable name="objet1.affiche()"/><finligne/>
					</exemplejavascript>
					<texte>Cela permet de ne pas avoir à répéter le nom de l'objet défini, réduit donc les risques d'erreur et facilite la maintenance et la reprise ultérieure du code. Attention, dans l'exemple précédent, les lignes dans le littéral sont séparées par des virgules, et les définitions utilisent des <code>:</code> et non des points-virgule.</texte>
				</paragraphe>
				<exercice titre="Définitions simples d'objets" ancre="exoDéfObjets">
					<enonce href="exercices/exo25.html"/>
					<correction href="exercices/exo25cor.html" description="définition d'un objet"/>
					<correction href="exercices/exo25cor_2.html" description="définition d'un littéral objet"/>
				</exercice>
			</section>
			<section titre="Interdire la manipulation de certaines propriétés&#160;: l'encapsulation" ancre="encapsulation">
				<paragraphe>
					<texte>Il est possible d'aller encore un peu plus loin. En effet, il n'est parfois pas souhaitable que certaines propriétés d'un objet soient accessibles de l'«&#160;extérieur&#160;» du script. En programmation orientée objet, on fait appel, dans une classe, à des membres dits privés et d'autres publics. Il est possible de faire de même en javascript, en utilisant le fait qu'une fonction <valeur>est</valeur> un objet, et que la portée des variables qui y sont déclarées y est limitée. Ainsi,</texte>
					<exemplejavascript>
						<instruction name="var"/><autres> </autres><variable name="livre"/><autres> = </autres><instruction name="new"/><autres> (</autres><fonction name="function"/><sautligne/>
						<bloc>
							<sautligne/>
							<autres>//Membres privés</autres><sautligne/>
							<instruction name="var"/><autres> </autres><variable name="chaine"/><autres> = "Test"</autres><finligne/>
							<fonction name="allerA"><variable name="Page"/></fonction><bloc><autres>(...)</autres></bloc><sautligne/>
							<autres>//Membres publics</autres><sautligne/>
							<instruction name="return"/><bloc><sautligne/>
							<variable name="ouvrir"/><autres> : </autres><fonction name="function"/><bloc><fonction name="allerA"><variable name="1"/></fonction><finligne/></bloc><sautligne/>
							</bloc>
						</bloc><sautligne/>
						<autres>)</autres>
					</exemplejavascript>
					<texte>Dans le code précédent, seule la méthode <code>ouvrir</code> est disponible. Cela permet en outre de rassembler en tête de la déclaration tout ce qui relève d'une configuration. Par exemple...</texte>
					<exemplejavascript>
						<instruction name="var"/><autres> </autres><variable name="livre"/><autres> = </autres><instruction name="new"/><autres> (</autres><fonction name="function"/><sautligne/>
						<bloc>
							<sautligne/>
							<autres>//Configuration à éditer</autres><sautligne/>
							<instruction name="var"/><autres> </autres><variable name="config"/><autres>=</autres>
							<bloc>
								<variable name="pageParDefaut"/><autres>:"1"</autres>
							</bloc>
							<finligne/>
							<autres>//Membres privés</autres><sautligne/>
							<instruction name="var"/><autres> </autres><variable name="chaine"/><autres> = "Test"</autres><finligne/>
							<fonction name="allerA"><variable name="Page"/></fonction><bloc><autres>(...)</autres></bloc><sautligne/>
							<autres>//Membres publics</autres><sautligne/>
							<instruction name="return"/><bloc><sautligne/>
								<variable name="ouvrir"/><autres> : </autres><fonction name="function"/><bloc><fonction name="allerA"><variable name="config.pageParDefaut"/></fonction><finligne/></bloc><sautligne/>
							</bloc>
						</bloc><sautligne/>
						<autres>)</autres>
					</exemplejavascript>
				</paragraphe>
				<exercice titre="Création d'objet avec membre privé" ancre="exoMembrePrivé">
					<enonce href="exercices/exo26.html"/>
					<correction href="exercices/exo26cor.html"/>
				</exercice>
			</section>
		</partie>
		<partie titre="Gestion des événements" ancre="gestionÉvénements">
			<section titre="Lancement d'un script au chargement de la page" ancre="lancement">
				<paragraphe>
					<texte>Nous avons jusqu'à présent chargé nos fonctions <code>Init</code> à l'aide de l'attribut <code>onload</code> de l'élément <code>body</code>. Cependant, il y a fort à parier que si l'on fait appel à d'autres scripts, le chargement de la page soit perturbé, et qu'il y ait des conflits. Il faut en effet au préalable s'assurer qu'aucun script ne se lance au même moment. S'il y en déjà un, il faut <valeur>ajouter</valeur> le nouveau script.</texte>
					<texte>Heureusement, la méthode <code>addEventListener</code> permet d'<valeur>ajouter</valeur> un gestionnaire à ceux qui existent éventuellement déjà. On peut alors l'utiliser, mais dans le cadre d'une méthode plus large, que nous appellerons <code>addLoadListener</code>, qui est compatible avec <logiciel lang="en">Internet Explorer</logiciel>&#160;:</texte>
					<exemplejavascript>
						<instruction name="function"/><autres> </autres><fonction name="addLoadListener"><variable name="gestionnaire"/></fonction><bloc>
							<instruction name="if"/><autres> (</autres><propriete name="window.addEventListener"/><autres>)</autres><bloc>
								<propriete name="window."/><fonction name="addEventListener"><variable name="'load'"/><variable name="gestionnaire"/><variable name="false"/></fonction><finligne/>
							</bloc><instruction name="else"/><autres> </autres><instruction name="if"/><autres> (</autres><propriete name="document.addEventListener"/><autres>)</autres><bloc>
								<propriete name="document."/><fonction name="addEventListener"><variable name="'load'"/><variable name="gestionnaire"/><variable name="false"/></fonction><finligne/>
							</bloc><instruction name="else"/><autres> </autres><instruction name="if"/><autres> (</autres><propriete name="window.attachEvent"/><autres>)</autres><bloc>
								<propriete name="window."/><fonction name="attachEvent"><variable name="'onload'"/><variable name="gestionnaire"/></fonction><finligne/>
								</bloc>
						</bloc>
					</exemplejavascript>
				</paragraphe>
				<exercice titre="Suppression de l'attribut onload" ancre="exooload">
					<enonce href="exercices/exo33.html"/>
					<correction href="exercices/exo33cor.html"/>
				</exercice>
			</section>
			<section titre="Contrôle des événements souris et clavier" ancre="sourisclavier">
				<paragraphe>
					<texte>Afin de préserver au maximum les possibilités d'interagir avec le document avec le clavier, il est recommandé de n'affecter des événements de clic que sur des éléments susceptibles de recevoir le «&#160;focus&#160;» clavier, à savoir <code>a</code>, <code>area</code> et les éléments de formulaire.</texte>
				</paragraphe>
				<exercice titre="Spécifier des événements clavier et souris" ancre="exoclaviersouris">
					<enonce href="exercices/exo35.html"/>
					<correction href="exercices/exo35_1cor.html" description="Événements souris"/>
					<correction href="exercices/exo35_2cor.html" description="Événements clavier et souris"/>
				</exercice>
			</section>
			<!--<section titre="Ajout de gestionnaires d'événements" ancre="ajoutGestionnaire">
				<paragraphe>
					<texte></texte>
				</paragraphe>
			</section>-->
		</partie>
	</corpus>
</chapitre>

