Le langage Javascript a mauvaise réputation du fait qu'un grand nombre d'utilisateurs s'en servent de manière intrusive ou à mauvais escient. Certains en arrivent même à croire que ce langage est néfaste et réfutent de l'employer, à tort. En effet, quelques règles suffisent à constituer de bons scripts ne détériorant pas l'accessibilité et s'insérant correctement au sein de vos pages sans causer le moindre conflit. Ce tutoriel est donc dévoué à vous montrer comment procéder et quelles sont les bonnes pratiques.
Une bonne page web doit être consultable et entièrement fonctionnelle sans Javascript. Ce langage est optionnel du fait que tout le monde n'en dispose pas. Aussi, Javascript est principalement utile lorsqu'il s'agit d'améliorer et d'étendre le comportement de vos pages.
Cela veut donc dire que vous ne disposez pas forcément de toutes les fonctionnalités de votre page si ce langage est indisponible. En revanche, chacun des éléments présents dans votre page fonctionne (liens, boutons, etc...). Si un bouton ne fonctionne pas sans Javascript, c'est que celui-ci n'a pas sa place dans le code XHTML et qu'il devrait être créé via le script. Les codes suivants illustrent ces propos.
<a href="#" onclick="window.print(); return false;">Imprimer</a>
Résultat (lien présent mais non fonctionnel sans Javascript) :
Imprimer
<script type="text/javascript"><!--
function fnInsertPrint()
{
// Création d'un lien
var oA = document.createElement('a');
oA.setAttribute('href', '#');
var oTxtA = document.createTextNode('Imprimer');
oA.appendChild(oTxtA);
// Affectation de la méthode print() au clic sur le lien
oA.onclick = function() { window.print(); return false; }
// Positionnement du lien dans la page
var oCont = document.getElementById('conteneur');
if(!oCont) return;
oCont.appendChild(oA);
}
// Lancement de la fonction fnInsertPrint()
fnInsertPrint();
//--></script>
Résultat (lien optionnel disponible avec JS) :
Nota : return false; empêche le navigateur de suivre le lien lorsque Javascript est activé et if(!oCont) return; annule l'insertion du lien si le conteneur récepteur n'existe pas. (bonne pratique à retenir)
Pour les mêmes raisons, on évite l'ajout de contenu via un document.write(). Les méthodes d'écriture doivent être réservées à ces contenus optionnels.
<script type="text/javascript"><!--
document.write('Salut, ça va ?');
//--></script>
Résultat (disponible uniquement avec JS) :
Quoi de pire qu'un code où les variables et méthodes ne veulent rien dire, où l'on ne trouve aucun commentaire et où l'indentation est quasi inexistante ? Entre nous, parmi les deux codes qui suivent, lequel préférez-vous lire ou modifier ?
<script type="text/javascript"><!--
function f(i,a){
if(!document.getElementById||!document.getElementsByTagName)return;
var c=document.getElementById(i),l=c.getElementsByTagName(a);
if(!l)return;var b=l.length-1;for(b;b>=0;b--){if(!l[b].href)
continue;window.alert(l[b].href);}
//--></script>
<script type="text/javascript"><!--
// Alerte de l'url pour chaque élément d'un conteneur
function fnRecapLinks(sId, sLink)
{
// Test des méthodes
if(!document.getElementById || !document.getElementsByTagName)
{
return;
}
// Tableau récapitulatif des éléments 'sLink' du conteneur 'sId'
var oCont = document.getElementById(sId);
var aLinks = oCont.getElementsByTagName(sLink);
if(!aLinks)
{
return;
}
// Lancement d'une alerte sur chaque item du tableau aLinks
var iI = aLinks.length - 1;
for(iI; iI >= 0; iI--)
{
if(!aLinks[iI].href)
{
continue;
}
window.alert(aLinks[iI].href);
}
}
//--></script>
Comme vous pouvez le constater, les variables et la fonction ont une syntaxe particulière. Elles commencent toutes par une ou deux lettres minuscules suivies de mots attachés commençant par une majuscule. C'est une convention de nommage appelée "Notation de type hongroise". Elle combine la "Notation Pascal" précédée du type de la variable (a pour array, s pour string, o pour object, i pour integer, fn pour function, v pour variant, etc...).
Aussi, il est conseillé, en développement, de commenter vos scripts mais ni trop ni trop peu. Ici, on est à la limite de l'excès. Avec un peu d'habitude, un commentaire par fonction devrait suffire. Lorsque vous y repasserez plusieurs mois après, vous n'aurez pas à réfléchir sur ce que vous aviez codé. Bien entendu, les scripts que vous mettez en ligne peuvent, quant à eux, être optimisés, compressés et se passer de ces commentaires mais gardez-en toujours une copie documentée.
Chaque utilisateur possède son propre mode de navigation. Certains utilisent le clavier, d'autres, la souris. Par exemple, cela signifie qu'il faut, dans le cas d'un drag-and-drop, activer le clic ou la pression sur une touche pour rendre votre script accessible. Un gestionnaire d'événement onclick est lancé par le clic de la souris mais aussi par la touche Entrée de votre clavier. Dans le même ordre d'idée, vous devez doubler vos onmouseover par des onfocus ainsi que les onmouseout par des onblur afin que chacun puisse disposer des méthodes affectées aux éléments, et ce, quelquesoit son mode de navigation. Pour illustrer cette notion, prenons le cas d'un rollover. Dans l'exemple suivant, que vous naviguiez au clavier ou à la souris, cela fonctionne.
<a href="#" id="test">lien test</a>
<script type="text/javascript"><!--
// Rollover sur un élément
function fnRollOver(sId, sCSS, sOverCSS)
{
// Test de la méthode utilisée
if(!document.getElementById) return;
// Identification de l'élément
var oElem = document.getElementById(sId);
if(!oElem) return;
// Définition de la classe CSS d'origine
oElem.className = sCSS;
// Comportement au survol ou à la prise du focus
oElem.onmouseover = function() { oElem.className = sOverCSS; };
oElem.onfocus = function() { oElem.className = sOverCSS; };
// Retour à l'état d'origine
oElem.onmouseout = function() { oElem.className = sCSS; };
oElem.onblur = function() { oElem.className = sCSS; };
}
// Lancement de la fonction fnRollOver()
fnRollOver('test', 'CSS', 'OverCSS');
//--></script>
Résultat :
lien test
Vos scripts ne doivent pas rediriger l'utilisateur automatiquement sur une autre page ni soumettre un formulaire complet sans qu'il y ait eu interaction avec l'utilisateur. Cela permet respectivement d'éviter que les moteurs de recherche vous pénalisent ainsi que la soumission prématurée d'un formulaire, vu que certaines technologies d'assistance ont des problèmes avec le gestionnaire d'événement onchange. Dans le cas des formulaires suivants, préférez donc le second.
<form id="Fruits1" action="verif.php" method="post">
<p>
<label>Indiquez votre fruit préféré :</label>
<select name="fruits"
onchange="document.getElementById('Fruits1').submit();">
<option value="Pomme">Pomme</option>
<option value="Poire">Poire</option>
<option value="Kiwi">Kiwi</option>
<option value="Banane">Banane</option>
</select>
</p>
</form>
Résultat :
<form id="Fruits2" action="verif.php" method="post">
<p>
<label>Indiquez votre fruit préféré :</label>
<select name="fruits">
<option value="Pomme">Pomme</option>
<option value="Poire">Poire</option>
<option value="Kiwi">Kiwi</option>
<option value="Banane">Banane</option>
</select>
<input type="submit" value="envoi" />
</p>
</form>
Résultat :
Tous les navigateurs n'implémentent pas ECMAScript, DOM ou BOM de la même manière. Certains n'interprètent rien et la plupart ne le font que partiellement. Quelques-uns se servent même de méthodes propriétaires. Il convient donc de tester chaque méthode utilisée avant de vous en servir. Si le navigateur comprend la méthode, on le laisse continuer, sinon, on arrête le script ou on sort de la fonction. De la même manière, vous pouvez tester que les variables envoyées ont bien la valeur attendue.
Par exemple, supposez que vous souhaitiez être alerté des urls de chaque élément d'un conteneur lorsqu'ils en possèdent, vous pouvez placer dans votre code les tests suivants :
<script type="text/javascript"><!--
function fnTest(sId, sElem)
{
// On arrête la fonction si ces méthodes ne sont pas reconnues
if(!document.getElementById || !document.getElementsByTagName)
{
return;
}
var oCont = document.getElementById(sId);
var aElem = oCont.getElementsByTagName(sElem);
// ou si le tableau d'élément(s) n'existe pas
if(!aElem) return;
// Sinon, on lance la fonction fnRecapLinks
return fnRecapLinks(aElem);
}
function fnRecapLinks(aElem)
{
var iI = 0;
var iTab = aElem.length;
for(iI; iI < iTab; iI++)
{
// On passe cet élément si l'url n'existe pas
if(!aElem[iI].href) continue;
window.alert(aElem[iI].href);
}
}
//--></script>
Vous pourriez bien sûr ajouter d'autres tests mais rien ne sert d'en faire trop; ce qui compte est d'éviter les cas risqués.
Une des premières règles d'accessibilité est de ne pas forcer l'utilisateur ou le navigateur à suivre vos propres règles. C'est à vous de vous adapter et non l'inverse. Par exemple, vous cliquez habituellement sur des liens ou des boutons. Si vous naviguez au clavier, vous avez sans doute remarqué qu'on observe le déplacement du focus; c'est une aide non négligeable pour la navigation. Si vous décidez d'attribuer une action au clic sur un paragraphe, celui-ci ne fait pas partie de la liste des éléments répertoriés par le focus car vous avez modifié son rôle. Vous êtes donc dépendant de l'interface utilisateur puisque la souris est obligatoire pour y accéder.
L'alerte qui apparaît en cliquant sur ce paragraphe est inaccessible via la navigation au clavier.
C'est un exemple parmi tant d'autres mais si vous choisissez malgré tout d'affecter un rôle différent à votre élément, vous vous exposez aux mauvaises prises en charge du navigateur.
Toute variable déclarée en dehors d'une fonction est globale, c'est-à-dire accessible et modifiable depuis n'importe quelle partie du script. Si vous n'êtes pas le seul à travailler sur un projet ou que les scripts sont nombreux, le risque de modification accidentelle de ces variables est grand. Par ailleurs, toute variable déclarée au sein d'une fonction sans le mot-clé var est potentiellement accessible par tout ce qui suit l'exécution de cette fonction. Pour limiter la portée des variables aux fonctions qui les contiennent, le mot-clé var est donc essentiel.
Par exemple, si vous écrivez :
var sBonjour = "Salut !"; // Variable globale
window.alert(sBonjour);
function fnDefVar()
{
var sBonjour = "Coucou !";
}
fnDefVar();
window.alert(sBonjour);
function fnModifVar()
{
sBonjour = "Coucou !"; // Modification de la variable globale
}
fnModifVar();
window.alert(sBonjour);
Vous obtenez deux alertes "Salut !", alors que la troisième lance un "Coucou !", ce qui est rarement ce que vous recherchiez. En évitant au maximum les variables globales et en ajoutant systématiquement le mot-clé var, vous éviterez au mieux les interférences.
Dans le même ordre d'idée, si vous vous servez d'une fonction au sein d'une propriété d'un objet, vous pouvez écrire :
var fnAlertVar = function()
{
var sBonjour = "Coucou !";
window.alert(sBonjour);
};
fnAlertVar();
Prenons un cas très simpliste mais qui explique correctement la démarche à suivre :
<a href="coucou.htm"
style="color: red;"
onclick="alert('coucou !'); return false;">
Hello world !
</a>
Comme vous l'aurez compris, ce code alerte l'utilisateur d'un "coucou !" lorsqu'on clique sur le lien. Doter toutes vos balises de ces attributs alourdit considérablement vos pages. La sémantique en souffre ainsi que la maintenance. Le langage CSS nous a montré comment externaliser les styles en accédant directement à la balise ou en mettant un identifiant ou une classe afin de lier la propriété de style au lien. Cela comporte de nombreux avantages tels que l'amélioration de la sémantique, du poids, de la portabilité, de la maintenance et j'en passe. Et bien devinez quoi ? On peut en faire de même avec DOM, avec les mêmes avantages. Considérons que nous avons plusieurs liens disséminés sur la page, nous n'écrivons plus que :
<a href="coucou.htm" class="alert">
Hello world !
</a>
Pour accéder au lien via CSS, vous mettez dans votre feuille de style:
a.alert { color: red; }
Pour accéder au lien via Javascript, vous pouvez vous servir de la méthode getElementsByTagName. On crée donc un fichier à part où on place un objet comme ci-dessous tout en prenant soin de charger la méthode une fois la page XHTML interprétée. Cela donne :
// Alerte au clic sur des éléments particuliers
function Alert(sA)
{
if(!document.getElementsByTagName) return;
var oA = document.getElementsByTagName(sA);
if(!oA) return;
var iI = oA.length - 1;
for(iI; iI >= 0; iI--)
{
if(oA[iI].className == 'alert')
{
oA[iI].onclick = function()
{
window.alert('coucou');
return false;
};
}
}
}
// Initialisation une fois la page chargée
window.onload = function()
{
Alert('a');
};
On retrouve ici la même fonction que précédemment mais vous pouvez maintenant séparer votre code Javascript du code XHTML en l'incluant dans un fichier externe (Alert.js par exemple) et en le liant à votre page via la déclaration suivante :
<script type="text/javascript" src="Alert.js"></script>
Comme nous nous servons de l'attribut class pour identifier nos liens et vu que la page XHTML est complètement interprétée lorsque le script est lancé, nous pouvons placer notre déclaration dans l'entête head du document. Dorénavant, à chaque classe "alert" rencontrée sur un lien, votre alerte se lancera. Il est donc inutile de répéter les codes CSS et JS dans la page XHTML.
Aussi, lorsque vous utilisez un gestionnaire d'événement (tel que onload ou onclick pour l'exemple décrit ci-dessus), vous devez lui affecter une propriété telle que la définition d'une fonction et non directement l'éxécutable sous peine de ne pas fonctionner (onload) ou de transmettre l'argument de la fonction sans attendre l'événement (onclick).
Javascript est un langage basé sur des objets. D'un point de vue général, on assimile un objet à une collection de données avec lesquelles on peut avoir diverses interactions. Un objet peut être pourvu de propriétés (des variables spécifiques à l'objet) et de méthodes (des fonctions que seul l'objet peut invoquer). Par exemple :
// La propriété length dépend du tableau aTab
var aTab = aTab.length;
// La propriété alert dépend de l'objet window
window.alert("coucou");
L'objet window est appelé objet global puisqu'il regroupe tout ce qui se trouve dans votre fenêtre. alert() est une méthode appartenant à cet objet, ce qui assure que seul window peut appeler cette fonction.
Afin de suivre la même logique, vous pouvez créer un objet en premier lieu dans votre script. Vous passez ensuite une fonction en tant que méthode de l'objet, ce qui permet, entre autres, de la protéger du fait que seul cet objet en dispose.
L'exemple précédent devient alors :
// Définition d'un objet
var oUtil = new Object;
// Définition d'une propriété (méthode) de l'objet
oUtil._Alert = function()
{
var sBonjour = "Coucou !";
window.alert(sBonjour);
}
// Appel de la méthode
oUtil._Alert();
Seul l'objet oUtil peut éxécuter la méthode _Alert. Du coup, si la méthode _Alert fait partie d'un autre script, il n'y a plus d'interaction. Pour différencier les méthodes de vos propres objets, vous pouvez placer un tiret bas sur chacune d'elles.
Aussi, Javascript permet l'utilisation des littéraux. Ceux-ci ne sont autres qu'une forme abrégée d'une expression afin de la rendre plus lisible. Par exemple :
var sTxt = new String("coucou");// équivaut à :
var sTxt = "coucou";
var aTab = new Array;// équivaut à :
var aTab = [];
var oObj = new Object;// équivaut à :
var oObj = {};
// etc...
En utilisant les littéraux, l'écriture équivalente à notre objet est :
// Définition d'un objet
var oUtil =
{
// Définition d'une propriété (méthode) de l'objet
_Alert: function()
{
var sBonjour = "Coucou !";
window.alert(sBonjour);
}
};
// Appel de la méthode
oUtil._Alert();
On y retrouve la méthode de notre objet exprimée à l'aide d'un label (_Alert) suivi de deux points et d'une valeur. Vu qu'il s'agit d'une méthode, on lui affecte une ou plusieurs instructions que l'on encapsule dans une fonction.
Si vous souhaitez ajouter plusieurs propriétés à votre objet, vous devez les séparer par une virgule.
Aussi, la chaîne texte "Coucou !" n'est pas, à proprement parler, une variable mais plutôt une constante dans ce script. Si vous placez les constantes directement en tant que propriétés de l'objet, vous pouvez les réutiliser pour d'autres méthodes. Cela donne :
var oUtil =
{
sBonjour: "Coucou !", // Constante définie en tant que propriété
_Alert: function()
{
window.alert(oUtil.sBonjour); // Accès à la constante
}
};
oUtil._Alert();
Une propriété accepte n'importe quel type ( string, array, function, etc... ), comme n'importe quelle variable.
Pour rendre la méthode plus générique, et donc réutilisable, il est préférable de passer les variables en tant qu'arguments de la fonction comme dans l'exemple ci-dessous :
var oUtil =
{
sBonjour : "Coucou !",
// Définition de la variable en tant qu'argument de la méthode
_Alert: function(sText)
{
window.alert(oUtil.sBonjour + sText);
}
};
// Exemples d'utilisation de notre méthode
oUtil._Alert(" Ca va ?");
oUtil._Alert(" Comment tu t'appelles ?");
Si votre méthode comporte plusieurs arguments, vous devez les séparer par une virgule, de la même manière que pour les fonctions.
Cette écriture rend vos scripts plus lisibles, plus sûrs et plus aisés à maintenir. Elle reste aussi dans l'esprit de JavaScript qui est, rappelons le, un langage essentiellement basé sur des objets.
Parmi vous, certains ne se limitent pas à un site unique. Une bonne méthode étant réutilisable, l'idée est alors de constituer une source de méthodes utilitaires. Ainsi, lorsque vous créez votre script, vous pouvez piocher dans cette bibliothèque pour créer vos objets sans avoir à charger systématiquement un fichier de quelques centaines de kilo-octets avec une foule de codes inutiles.
Si certaines de vos méthodes comportent des codes lourds et redondants, faites-en de nouvelles méthodes. Plus celles-ci sont courtes et plus on les comprend facilement. La maintenance s'en retrouve facilitée et votre code est plus léger.
A titre d'exemple, créons une liste ul contenant 2 éléments li avec leur texte puis insérons les dans un conteneur div vide situé dans le code XHTML. Si on part bille en tête, on crée nos éléments les uns à la suite des autres et on obtient le code suivant :
var oO =
{
_InitList1: function()
{
oO._List();
},
_List1: function()
{
// Test des méthodes utilisées
if(!document.getElementById ||
!document.createElement ||
!document.createTextNode) return;
// Définition du conteneur récepteur de la liste
var oDiv = document.getElementById('ContList');
if(!oDiv) return;
// Création de la liste
var oUl = document.createElement('ul');
var oLi1 = document.createElement('li');
var oTxt1 = document.createTextNode('Texte 1');
var oLi2 = document.createElement('li');
var oTxt2 = document.createTextNode('Texte 2');
// Constitution de l'arbre DOM
oLi1.appendChild(oTxt1);
oUl.appendChild(oLi1);
oLi2.appendChild(oTxt2);
oUl.appendChild(oLi2);
oDiv.appendChild(oUl);
}
};
// Lancement de la méthode d'initialisation
window.onload = oO._InitList1;
Résultat (visible uniquement avec JS activé) :
Superbe ! Ca marche, on obtient bien notre liste...
Si maintenant, je vous dis d'ajouter une dizaine d'éléments de liste supplémentaires, allez-vous continuer à multiplier les lignes ainsi ? Et si, par un malheureux hasard, je vous disais de créer une seconde liste avec des textes différents, allez-vous recréer une méthode supplémentaire ? C'est un peu lourd, n'est-ce pas ?
Comme nous l'avons vu dans le chapitre du modèle objet, nous pouvons déjà reporter toutes nos constantes en tant que propriétés. Si nous en avons un grand nombre, il peut être judicieux de grouper chaque type de constante dans un tableau plutôt que de faire une liste interminable de propriétés pour chaque chaîne texte.
Par ailleurs, nous voyons ici que la création des éléments de liste li est répétitive. Cette partie peut donc avoir sa propre méthode.
Enfin, votre objet contiendra certainement de nombreuses méthodes où des document.getElementById, document.getElementsByTagName et autres ressortiront régulièrement. Il est dès lors intéressant de faire une méthode test généraliste plutôt que de tout réécrire pour chaque méthode.
Pour illustrer ces propos, étudiez donc le code suivant :
var oO =
{
// Test
_Test: function()
{
var iI = arguments.length - 1;
for(iI; iI >= 0; iI--) if(!arguments[iI]) return false;
return true;
},
// Constitution et intégration d'une liste dans le document
_List: function(cont, list, elem, text)
{
var oCont = document.getElementById(cont);
if(!oCont) return;
var oList = document.createElement(list);
var iI = 0;
var iTab = text.length;
for(iI; iI < iTab; iI++)
{
oList.appendChild(oO._Elem(elem, text[iI]));
}
oCont.appendChild(oList);
},
// Création d'un élément contenant du texte
_Elem: function(elm, txt)
{
var oElem = document.createElement(elm);
var oTxt = document.createTextNode(txt);
oElem.appendChild(oTxt);
return oElem;
},
// Conteneur
Id: 'ContList2',
// Elements
Tag: ['ul', 'li'],
// Textes
Text: ['Texte 1', 'Texte 2', 'Texte 3', 'Texte 4', 'Texte 5']
};
// Test des méthodes suivantes
if(oO._Test(document.getElementById, document.createElement, document.createTextNode))
{
// Initialisation de la méthode oO._List
window.onload = function()
{
oO._List(oO.Id,oO.Tag[0],oO.Tag[1],oO.Text);
}
}
Résultat (visible uniquement avec JS activé :
Le résultat est identique mais la différence majeure est que :
A première vue, vous pourriez trouver cela plus complexe mais regardez donc les possibilités. Copiez le code puis :
Comme vous le voyez, on obtient un script bien plus flexible, facile à maintenir et surtout plus rentable à la longue. Les méthodes sont courtes, donc plus lisibles, et elles se fondent sur leurs arguments ce qui permet de s'en resservir. Aussi, prenez le temps nécessaire à la bonne compréhension de ce code, je peux vous assurer que ce travail ne sera pas à perte. De manière générale, tentez toujours d'optimiser vos méthodes, c'est le seul moyen d'obtenir une bonne bibliothèque.
Jusqu'à maintenant, nous avons vu comment améliorer nos scripts en séparant la structure, la présentation et le comportement, en utilisant le modèle objet et en rendant nos méthodes utiles à de nombreuses occasions. Dans cette dernière phase, nous avons regroupé nos données sous forme de propriétés de l'objet. Nous pouvons faire mieux en séparant les données de notre script. Certains d'entre vous penseront dès lors XML mais dans ce cas, vous devrez soit transformer votre fichier XML pour le rendre interprétable par le script soit utiliser des méthodes propres au contenu XML. Est-ce la meilleure méthode ? Vous l'aurez deviné, la réponse est non.
Le soucis avec XML est qu'il structure vos données, ce qui nécessite un moteur de transformation tel que Xalan ou Saxon associé à une feuille de style XSLT ou des méthodes spéciales pour convertir vos données au bon format. C'est plutôt fastidieux pour ce que nous souhaitons faire et nous n'avons généralement pas besoin de cette structure pour transmettre des données. C'est pourquoi un autre format d'échange semble plus en vogue : JSON ou Javascript Object Notation. L'avantage de celui-ci est qu'il est directement interprétable par Javascript, ce format étant une sous-classe de ce langage, et qu'il est bien moins lourd qu'un fichier XML lorsqu'il s'agit d'envoyer des données. Cela ne met pas pour autant XML au placard, ce dernier ayant bien d'autres utilités mais si vous vous mettez à l'Ajax, mieux vaut y réfléchir à deux fois avant de foncer tête baissée.
Reprenons donc les propriétés où nous avions regroupé nos données et créons un fichier data.json . Cela prend la forme suivante :
{
"Id": "ContList2",
"Tag": ["ul", "li"],
"Text": ["Texte 1", "Texte 2", "Texte 3", "Texte 4", "Texte 5"]
}
Pour lier le fichier JSON à notre script, nous transformons notre fichier script.js en fichier script.js.php puis nous faisons une inclusion PHP. Nous créons ensuite une nouvelle variable pour accueillir notre objet. Cela donne :
<?php
header("Content-type: text/javascript; charset=iso-8859-1");
?>
// Objet Utilitaire
var oO =
{
// Ici se trouvent nos méthodes précédentes
};
// Objet de données
var o$ =
<?php include "data.json"; ?>;
Pour appeler chaque propriété en tant qu'argument de la méthode oO._List, nous faisons un tableau associatif et nous prenons soin de vérifier que l'objet de données existe bien, ce qui donne :
// Initialisation de la méthode oO._List
window.onload = function()
{
if(o$) oO._List(o$["Id"], o$["Tag"][0], o$["Tag"][1], o$["Text"]);
}
A noter que cette écriture est parfaitement équivalente à la syntaxe pointée. Pour vous en convaincre, faites donc le test avec :
// Initialisation de la méthode oO._List
window.onload = function()
{
if(o$) oO._List(o$.Id, o$.Tag[0], o$.Tag[1], o$.Text);
}
Le script fonctionne toujours.
Nos données sont bien en externe dans un fichier qui leur est propre. Ceci facilite l'échange avec d'autres langages, PHP entre autres.
Sans doute avez-vous déjà été confronté au problème ? Vous récupérez un script que vous souhaitez ajouter à votre bibliothèque Javascript existante et là, paf ! Votre script ou celui que vous venez de récupérer ne fonctionne plus. De même, vous décidez d'affecter un second gestionnaire d'événement onclick, onblur ou autres et, tout à coup, le comportement n'est pas celui que vous attendiez. C'est tout le problème de ce genre de gestionnaires. Un onload, onclick, etc... ne permet d'assigner qu'une fonction à la fois. Pour contourner ce problème vous pouvez affecter une fonction sur le gestionnaire comme suit :
window.onload = function()
{
oO.method1();
oO.method2();
};
Le soucis, c'est que votre script monopolise window.onload et pose donc le même problème que celui que vous venez de récupérer. Pour contourner celà, Simon Willison (http://simon.incutio.com/) a écrit la fonction suivante :
function addLoadEvent(func)
{
var oldonload = window.onload;
if(typeof window.onload != 'function')
{
window.onload = func;
}
else
{
window.onload = function()
{
oldonload();
func();
}
}
}
En se servant de cette fonction pour affecter la méthode de lancement de chacun de vos scripts, ceux-ci ne s'approprient plus le window.onload. Pour chaque appel de méthode, cela donne :
addLoadEvent(oO.method1);
addLoadEvent(oO.method2);
Tout ceci repose sur les spécifications DOM-1 où, par exemple, l'événement qui survient est click et le gestionnaire, c'est à dire le lieu où l'événement est enregistré, est onclick.
Voyons un peu plus loin en s'initiant aux méthodes addEventListener, propre à DOM-2 et DOM-3, et attachEvent, propre à Internet Explorer. Je ne détaillerai pas toutes les subtilités de leur fonctionnement sur ce tutoriel car le but n'est pas ici de tout développer mais simplement de vous montrer la voie. Si le sujet vous intéresse, vous trouverez des liens complémentaires en fin de ce document.
Donc, en DOM-2, le concept est différent : vous devez jongler entre la cible de l'événement et ce qui va à la pêche à l'événement. La cible est là où se produit l'événement, généralement un élément HTML, et ce qui va à la pêche est la fonction qui joue avec cet événement. DOM-3 intègre, en plus, la notion de capture de l'événement mais vu que peu de navigateurs la comprennent, il est préférable de ne pas l'autoriser en mettant systématiquement sa valeur à false. Ainsi, vous vous assurez que tous fonctionnent dans le même mode. Etant donné qu'Internet Explorer ne fonctionne pas comme tout le monde et qu'il y a des navigateurs qui ne comprennent pas DOM, voici la fonction imaginée par Scott Andrew qui permet de prendre en charge divers modes de fonctionnement :
function addEvent(oElem, sEvType, fn, bCapture)
{
return oElem.addEventListener?
oElem.addEventListener(sEvType, fn, bCapture):
oElem.attachEvent?
oElem.attachEvent('on' + sEvType, fn):
oElem['on' + sEvType] = fn;
}
Parmi les arguments de cette fonction, vous avez oElem qui définit l'objet élément, sEvType qui définit le type d'événement, fn qui définit la fonction (méthode) et bCapture qui indique si on est en mode capturant ou non. Pour conclure, réécrivons donc le script précédent en intégrant cette fonction au sein des méthodes de l'objet utilitaire. On obtient donc, avec tout ce qu'on a vu précédemment, les fichiers suivants :
fichier index.php
<?php
header("Content-type: text/html; charset=iso-8859-1");
?>
<!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>Fichier test</title>
<script type="text/javascript" src="script.js.php"></script>
</head>
<body>
<div id="ContList"></div>
</body>
</html>
fichier data.json
{
"Id":
"ContList",
"Tag":
[
"ul",
"li"
],
"Text":
[
"Texte 1",
"Texte 2",
"Texte 3",
"Texte 4",
"Texte 5"
]
}
fichier script.js.php
<?php
header("Content-type: text/javascript; charset=iso-8859-1");
?>
// Objet Utilitaire
var oO =
{
// Méthode de connection
_Connect:
function(oElem, sEvType, fn, bCapture)
{
return oElem.addEventListener?
oElem.addEventListener(sEvType, fn, bCapture):
oElem.attachEvent?
oElem.attachEvent('on' + sEvType, fn):
oElem['on' + sEvType] = fn;
},
// Test
_Test:
function()
{
var iI = arguments.length - 1;
for(iI; iI >= 0; iI--) if(!arguments[iI]) return false;
return true;
},
// Constitution et intégration d'une liste dans le document
_List:
function(cont, list, elem, text)
{
var oCont = document.getElementById(cont);
if(!oCont) return;
var oList = document.createElement(list),
iI = 0,
iTab = text.length;
for(iI; iI < iTab; iI++) oList.appendChild(oO._Elem(elem, text[iI]));
oCont.appendChild(oList);
},
// Création d'un élément contenant du texte
_Elem:
function(elm, txt)
{
var oElem = document.createElement(elm),
oTxt = document.createTextNode(txt);
oElem.appendChild(oTxt);
return oElem;
}
};
// Objet de données
var o$ =
<?php include "data.json"; ?>;
// Test des méthodes suivantes
if(oO._Test(document.getElementById, document.createElement, document.createTextNode))
// Initialisation de la méthode oO._List si le test est passé
oO._Connect(window, 'load', function()
{
if(o$) oO._List(o$["Id"], o$["Tag"][0], o$["Tag"][1], o$["Text"]);
}, false);
Et voilà, bonne programmation à toutes et à tous. ![]()