PHP Solutions : Concevoir une application pour le Web avec Ajax
Article paru dans le magazine PHP Solutions (Mai 2008). Attention cet article n’est pas soumis à la licence Creative Commons : tous les droits de cet article sont réservés.
Ajax répond à une demande : c’est l’un des outils principaux permettant de concevoir une application riche apportant une nouvelle expérience à l’utilisateur. Elle est devenue une technologie très à la mode, mais qui reste malheureusement assez mal utilisée. Nous donc allons voir dans quels cas elle est utile, et quelques règles fondamentales de la conception d’application avec Ajax.
Cet article explique :
- en quoi consiste une application web et la place d’Ajax dans son architecture
- comment concevoir des interfaces riches avec Ajax et le DOM
Ce qu’il faut savoir :
- maîtriser la conception d’applications avec PHP
- avoir des bonnes bases de XML, XHTML et Javascript
Ajax permet de concevoir des applications Web
C’est assurément à la mode : tout le monde en parle comme la technologie qui révolutionne le web. Concrètement, Ajax permet de mettre à jour le contenu de la page sans la recharger complètement. De ce simple fait, on peut aujourd’hui concevoir des sites qui deviennent particulièrement puissants, proposant de nombreuses fonctions à l’utilisateur directement dans son navigateur. La navigation sur le site s’approche plutôt de la manipulation d’un logiciel, le look en plus. On aime alors parler de RIA, pour Rich Internet Application.
Une RIA est typiquement composée d’une interface cliente gérée par le navigateur, conçue avec Ajax, et d’un support arrière gérant les données sur le serveur, programmée dans un langage de scripts Web plus traditionnel comme PHP.
Ainsi, l’essentiel de l’application étant sur internet, elle devient totalement portable. D’une part, l’utilisateur n’a plus rien à installer, et peut retrouver son environnement de travail, automatiquement mis à jour, sur chaque poste accueillant un navigateur récent sans même avoir à se soucier du système d’exploitation qu’il utilise. D’autre part, l’application bénéficie d’un contenu renouvelé en permanence, grâce aux administrateurs ou à une communauté d’utilisateurs dont les contributions sont centralisées dans une seule base de données.

Au final, Ajax permet de lier les avantages d’internet aux logiciels classiques, et apporte à l’utilisateur un confort que l’on trouvait sur internet grâce à Flash ou Java, qui étaient moins accessibles.
D’un point de vue technique, Ajax est basé sur Javascript, un langage interprété par le navigateur qui permet de modifier son comportement en fonction des actions de l’utilisateur. Des événements sont surveillés par le navigateur qui déclenche alors une série d’actions. Dans le cas d’Ajax, on envoie une requête au serveur, qui retourne du contenu au format XML, c’est ainsi que l’ont peut faire varier le contenu d’une manière sensiblement plus agréable pour l’utilisateur.
Ajax n’est pas un gadget
Il est impératif d’avoir en tête que les avantages d’Ajax sont directement liés à ses inconvénients. Premièrement, la communication entre le client et le serveur s’effectue toujours avec le protocole HTTP, donc de manière ponctuelle. L’organisation de l’application est très dépendante de cette limite : il faut en particulier prévoir comment gérer les données et les objets persistants entre chaque échange client-serveur. Ce problème de conception, comme d’autres, existe bien sûr depuis qu’il existe des applications dynamiques sur internet, mais Ajax apporte un niveau de complexité supplémentaire non négligeable, notamment en terme de sécurité.
D’autre part, Ajax est une jeune technologie, donc au nombreux pré-requis techniques. Il est nécessaire que le visiteur possède un navigateur récent, configuré de manière à ce que le Javascript et les cookies soient activés. Les navigateurs alternatifs, comme les navigateurs mobiles ou les lecteurs d’écrans (navigateurs vocaux ou en braille) sont souvent limités en fonctionnalités et ne supportent généralement pas le Javascript, donc très rarement Ajax. Les appareils mobiles, tout comme les vieux ordinateurs sont généralement peu puissants et ne possèdent pas toujours suffisamment de mémoire pour supporter des manipulations dynamiques lourdes, les pages risquent donc de ne pas pouvoir s’afficher.
L’utilisateur doit aussi avoir une bonne connexion à internet à disposition : illimitée, car les pages modifiées par Ajax ne sont pas consultables hors-ligne, et avec un bon débit car les fichiers Javascript sont souvent lourds. Il faut garder en tête que tout le monde ne dispose pas d’une connexion ADSL dans toute circonstance : certaines personnes n’ont accès à internet que depuis leur lieu de travail par exemple, ou d’autres utilisent des connexions mobiles de type 3G, qui sont facturée à la durée de connexion et au volume des données transférées.
Ces contraintes limitent le nombre de visiteurs potentiels sur le site, et de fait, brident l’accès à l’information que vous souhaitez distribuer. Comme nous l’avons déjà évoqué, l’utilisation d’Ajax doit donc être justifiée, et pourra être un choix judicieux pour une application interne à une entreprise où le parc informatique est maîtrisé ou si vous ciblez un profil d’utilisateur particulier.
Ajax n’a donc pas systématiquement sa place dans l’architecture d’un site internet. Par exemple, le site promotionnel d’une marque ou d’une entreprise n’a généralement besoin que d’une série de pages HTML élégantes et bien conçues. Le site internet d’un journal, même si il est conçue sur une base dynamique côté serveur (pour la mise à jour des articles en temps réel par exemple) doit être composé de pages claires et accessibles, et doit donc limiter les fonctionnalités secondaires, à moins de proposer une alternative minimaliste qui permet quand même l’accès à l’information.
Il ne faut tout de même pas se méprendre : Ajax est loin d’être parfait. L’un des défauts majeurs trop souvent oublié, c’est que le développeur doit, d’une certaine manière, réinventer la roue en programmant manuellement l’ensemble des opérations de transaction par HTTP : requête, traitement des erreurs, réception et traitement du flux de données entrantes, alors que ce est travail normalement effectué en totalité par le navigateur.
Ce n’est pas non plus une technologie normalisée, les navigateurs ne l’implémentent pas tous de la même manière et le développeur perd beaucoup de temps à rendre son code compatible avec la majorité des configurations. Il est possible de palier à ce problème et de lisser l’implémentation d’Ajax grâce à l’un des nombreux frameworks disponibles, sur lesquels nous reviendront. Mais ceux-ci sont généralement très lourd, que ce soit par rapport au poids du fichier ou la mémoire utilisée par le navigateur lors de l’interprétation de ce framework.
Avec X/HTML5, actuellement en cours d’élaboration, on peut espérer que ces principales lacunes disparaissent et que les navigateurs gèrent de mieux en mieux l’implémentation de technologies et outils pour les applications riches.
L’organisation d’une application Web
Classiquement, l’utilisateur entre une adresse dans son navigateur, le navigateur se charge d’émettre la requête HTTP (1.1) complète au serveur qui renvoie le document (généralement HTML ou XML). Le navigateur, en interprétant la page effectue une série d’appels pour chaque fichier (feuilles de styles, images, Javascript) attaché au premier document. Lorsque l’utilisateur émet (formulaire) ou veut obtenir (lien hypertexte) de nouvelles données, le navigateur renvoie une requête, traitée par l’application sur le serveur et la nouvelle page est transmise. L’ensemble du traitement de l’application s’effectue sur le serveur, et la page retournée et en quelque sorte un reflet des données de l’application. L’interactivité est très faible puisque le serveur prend le relais à chaque action de l’utilisateur. C’est ce que les spécialistes aiment aujourd’hui appeler l’architecture “Web 1.0”. C’est un peu snob, mais c’est comme ça !
La différence avec les applications Web 2.0, c’est qu’il est possible d’utiliser une autre manière pour discuter avec le serveur : un objet introduit dans le langage Javascript au centre d’Ajax : XMLHttpRequest.
Comme son nom l’indique, cet objet contient un certain nombre de propriétés et méthodes permettant d’envoyer et recevoir une requête HTTP manuellement. La page n’est plus rechargée dans sa totalité car la requête est traitée au cours de l’exécution des scripts de la page courante. Dans le cas d’Ajax, le résultat de la requête est un document de données au format XML, qui bénéficie d’une compatibilité native avec le navigateur et bénéficie d’une API très pratique pour le manipuler : le DOM.
Nous allons donc étudier de plus près les différents aspects de l’utilisation de l’objet XMLHTTPRequest. Le code présenté est sommaire, et comme nous le verrons plus loin, le développeur ne traite pas directement avec l’objet mais utilise des frameworks apportant quelques facilités.
La requête HTTP
En utilisant la version 1.1 du protocole HTTP, c’est à dire la plus répandue aujourd’hui, une requête se présente sous cette forme :
GET /page.php?foo=yes&bar=no HTTP/1.1 Host: www.domaine.com User-Agent: Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.8.1.1) Gecko/20060601 Firefox/2.0.0.1 (Ubuntu-edgy) Accept: text/html, text/xml Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: Gzip, deflate Accept-Charset: UTF-8, ISO-8859-1 Referer: http://martiusweb.net/index.php If-Modified-Since: Wed, 27 Apr 2005 17:11:24 GMT [...]
Il est intéressant de connaître ce format, même si il est assez rarement utilisé par le développeur. En effet, pour analyser votre application ou la débugguer, vous gagnerez beaucoup de temps en regardant directement le contenu des requêtes. Certains outils de débuggage permettent par ailleurs de l’exploiter pour détecter d’éventuels problèmes.
La première ligne contient toujours trois informations. En premier, la méthode de transfert, généralement GET ou POST, avec lesquels vous devriez être familier. Il existe d’autres méthodes (telles que PUT), mais elle ne sont pas (ou peu) supportées par les navigateurs, et donc inexistantes avec Ajax. En deuxième, on trouve l’adresse du document à consulter sur le serveur avec ses éventuels paramètres. Enfin on précise le protocole utilisé et sa version.
Les lignes suivantes concernent les en-têtes. Nous allons présenter les principales, que l’on retrouve presque systématiquement.
Hostcorrespond au domaine hôte du document. Parfois il est absent des éléments d’en-tête car directement intégré dans l’adresse de la requête.User-Agentpermet au client de se présenter. Il indique le système d’exploitation utilisé avec plus ou moins de précision sur l’architecture du système et le navigateur utilisé.Acceptprécise les formats de données acceptés. Cet élément est particulièrement important actuellement, car il est un indicateur des différences de compatibilité entre les navigateurs.Accept-Languageindique ensuite les langues préférées par l’utilisateur (selon sa configuration), dans l’ordre de priorité. Cet élément d’en-tête très utile est généralement oublié par les développeurs de sites en plusieurs langues, qui redirigent systématiquement le visiteur vers une page lui demandant de choisir sa localisation, alors que l’information est déjà disponible. Pour le confort de l’utilisateur, on peut commencer par tester l’en-tête, lui proposer de modifier ce choix en le stockant dans un cookie ou enfin, utiliser la langue par défaut du site si les autres tentatives de définition s’avèrent infructueuses.Accept-Encodingdéfinit si le navigateur supporte une méthode de compression des données. Généralement, le serveur web se charge lui-même de compresser les pages si le client l’accepte.Accept-Charsetindique le type d’encodage des caractères supportés par défaut. Il est peu utilisé, car généralement un site internet n’en propose qu’un seul. L’internationalisation des applications incite généralement les développeurs à utiliser le système unicode (UTF-8), qui couvre la majorité des alphabets. Cependant, un caractère est alors codé sur plusieurs octet, un paragraphe de données sera donc plus volumineux.Refererindique la page sur laquelle l’utilisateur se situe au moment de la requête, autrement dit, la page qui a conduit l’utilisateur à votre page. Cet élément n’est pas toujours présent, ou parfois vide, mais il est particulièrement utile pour emmètre des statistiques d’audience ou contrôler l’origine de l’utilisateur pour des raisons de sécurité.If-Modified-Sinceindique au serveur qu’il est utile de renvoyer le contenu du document à la condition qu’il ai été modifié depuis la date indiquée. Pensez à utiliser cet élément autant que possible, car il peut être très pratique pour préserver la bande passante de transferts inutiles, sans pour autant implémenter des techniques lourdes pour tester.
De nombreux autres éléments peuvent être ajoutés à l’en-tête, je vous invite à consulter les documentations à ce sujet sur internet.
La réponse est composée des en-têtes et du contenu du document lui même. La séparation est marquée par une ligne vide entre les deux parties. Voici un exemple de réponse :
HTTP/1.1 200 OK Date: Mon, 25 Feb 2007 14:16:00 GMT Server: Apache/2.0.0 With mod-php5, mod-python X-Powered-By: PHP/5.1.2 Content-Type: text/html; charset=UTF-8 Cache-Control: max-age=60 [...] Contenu du document
La réponse est composée des en-têtes et du contenu du document lui même. La séparation est marquée par une ligne vide entre les deux parties.
La première ligne de l’en-tête contient les informations sur le protocole, et le code de réponse. Ici 200 signifie que la
requête à parfaitement abouti. D’autres codes d’erreur courants sont par exemple 403, qui signifie que l’utilisateur n’est pas
autorisé à consulter le document, 404, qui signifie que le document recherché n’existe pas ou 500 dans le cas d’une erreur interne du serveur, qui cache généralement un problème de configuration du serveur web.
Les lignes suivantes sont d’autres éléments d’en-tête. Les information de date et de durée de validité , tels que Date ou
Cache-Control sont particulièrement utiles, encore une fois, pour minimiser le trafic superflu.
D’autres informations concernant le serveur sont délivrées dans la requête, comme Server ou X-Powered-By
On préfixe généralement les éléments dont le sens n’est pas défini par le protocole HTTP par X-. Sachez que vous pouvez ajouter, si vous en avez l’utilité, certains éléments d’en-tête propres à votre application.
Sachez également observer l’environnement d’exécution de votre application. Dans le cas d’un serveur web comme Apache, des traces des échanges sont archivés, notamment dans les fichiers log de l’application. Certains outils proposent par ailleurs de les analyser pour vous, comme analog sous Linux.
Plus d’informations sur HTTP et Apache
N’hésitez pas à vous documenter sur les subtilités du protocole HTTP et Apache, ou n’importe quel autre serveur web que vous souhaitez utiliser.
- http://www.w3.org/Protocols/
- http://httpd.apache.org/docs/2.0/fr/
- http://www.cybersdf.org/2005/07/09/128-le-http-pour-les-nuls
Le flux de données XML
Les données XML sont la réponse à la requête émise par Ajax. Le flux sera donc réceptionné par l’objet XMLHttpRequest avant que l’on puisse l’utiliser. La construction de votre document XML aura une très forte influence sur le reste de votre interface client, une étape de réflexion sur la façon dont vous souhaitez manipuler ces données est donc incontournable.
Dans un premier temps, identifiez clairement les données à transmettre. Regroupez-les en fonction de leur sens et de leurs liens. Un premier tri sera généralement la séparation entre les données métier de votre application et les éventuelles informations correspondant à la manière dont doit évoluer l’interface avec ces informations.
Il faut ensuite modéliser vos données en XML. En suivant la méthode rigoureusement, ça ne devrait pas poser trop de problèmes. Il faut garder en tête que le code lui même doit jouer le rôle de sa propre documentation, il est donc impératif de suivre la logique imposée par XML. A partir de ce moment, le plus gros du travail est fait. Les propriétés d’un élément se présentent logiquement sous forme d’attribut, et son contenu (si il existe) et placé entre la balise ouvrante et la balise fermante. Un attribut ne doit jamais être précisé dans un élément fils, à même titre que le contenu n’a pas sa place dans un attribut. Le code suivant illustre cette problématique avec un exemple un peu grossier.
<magazine> <articles> <article page="01" auteur="Martin Richard" auteurid="01"> <titre>Concevoir une application pour le web avec Ajax</titre> <chapitre><!-- ... --></chapitre> </article> </articles> </magazine>
Voici le même document XML, mieux formé :
<magazine> <article page="01" titre="Concevoir une application pour le web avec Ajax"> <auteur auteurid="01" nom="Martin Richard" /> <chapitre><!-- ... --></chapitre> </article> <article page="02" titre="Le modèle MVC"> <auteur auteurid="02" nom="Pierre Durand">Pierre Durand est ...</auteur> <chapitre><!-- ... --></chapitre> </article> </magazine>
La première grosse erreur est située dès la deuxième ligne, car l’élément <articles> n’est pas utile. On peut penser que
regrouper les différentes éléments identiques dans une même balise, pour les retrouver plus facilement par exemple, est une bonne idée, mais en manipulant le document plus tard, ce choix risque de surprendre, et coûtera une opération en plus pour accéder un niveau plus bas dans la hiérarchie. Une autre manière aurait été de regrouper les articles dans un élément <dossier> si le magazine est organisé de cette manière.
La deuxième erreur concerne la ligne suivante, et les deux attributs auteur et auteurid L’auteur n’est pas un attribut de l’article, mais un élément à part entière, il est donc plus logique de le considérer comme un élément fils de l’article, contenant deux attributs. Ici, il est même possible d’utiliser la balise <auteur> comme conteneur d’une description, par exemple. On peut envisager une autre manière d’exploiter la même syntaxe : si le document devait être un tri des articles par auteur, notre élément <auteur> aurait été parent de <article>.
Enfin, considérer le titre de l’article comme étant un élément n’est pas vraiment une erreur, mais puisqu’il ne contient pas le moindre attribut, il sera beaucoup plus simple d’y accéder si l’information est elle-même un attribut de l’élément article.
Si votre application à pour but d’être portable, ou que le format de votre document XML doit être échangeable, il devient nécessaire de fixer la grammaire de ce format, pour que tout le monde se comprenne. Pour fixer un format de document XML, il est possible d’utiliser un langage conçu pour par le W3C : le langage de DTD (pour DocType Definition). Cependant, la syntaxe est assez rigide et la prise en main n’est pas évidente. Une alternative normalisée assez courante, et supportée par PHP s’appelle RelaxNG. Ce langage est lui-même issu de XML, et sa syntaxe est plus accessible. Je vous invite à vous renseigner sur ces deux langages pour vous faire une idée de leur utilisation.
Gérer la transaction avec Javascript
Il y a plusieurs manière de traiter la requête HTTP, car les frameworks Ajax disponibles implémentent tous leur propre méthode et leurs propres règles. Nous allons ici nous contenter de voir le traitement d’une requête Ajax avec le code le plus simple possible :
/* On effectue un test qui permet d'utiliser l'objet * XMLHttpRequest (que l'on appellera XHR) en fonction * du navigateur. */ if(window.XMLHttpRequest) Ajax = new XMLHttpRequest(); else if(window.ActiveXObject) { try { Ajax = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { // Retour de l'exception, avec Internet Explorer } } /* On définie quelques règles de traitement de la requête */ Ajax.onreadystatechange = function() { if(Ajax.readyState == 4) traiterResultat(); else if(Ajax.readyState == 0) masquerMessageAjax(); else afficherMessageAjax(); /* En toute logique, nos fonctions traiterResultat(), * afficherMessageAjax() et masquerMessageAjax() seraient * plutôt les méthodes d'un objet. */ } function traiterResultat() { if(Ajax.status == 200) { // La requête est parfaitement passée, // on peut l'utiliser. } else { if(Ajax.status == 403 || Ajax.status == 404 || Ajax.status == 500) // on affiche un message d'erreur } Ajax.open('GET','page.php?param='+valParam, false); Ajax.send(null); }
En premier, il faut créer un objet XMLHttpRequest (que l’on appellera XHR), en vérifiant le navigateur utilisé, car Microsoft le considère comme un objet ActiveX.
Avant de lancer la requête, nous allons définir la politique qui permet de suivre son évolution. L’objet XHR met à disposition la propriété
XHR.readyState qui prend pour valeur un code numérique correspondant à un état de la requête. Les codes sont 0 quand la requête n’a pas été initialisée, 1 quand elle est en train de charger, 2 quand elle a été reçue, 3 pendant que le navigateur est en train de la traiter et 4 quand elle est terminée, et donc que nous allons pouvoir utiliser son résultat.
Pour surveiller les changements d’état, il existe un événement XHR.onreadystatechange auquel on attache une fonction qui va par exemple afficher un message d’attente tant que la requête n’est pas terminée et déclencher le traitement quand le code état sera 4. Le message d’attente doit être assez clair et amené sans ambiguïté (il ne doit pas être pris pour un message d’erreur par exemple), évitez par ailleurs d’utiliser des termes techniques qui risquent d’embrouiller. Généralement, un message pour les trois états intermédiaires suffisent. De nombreux générateurs d’images d’attentes animées sont disponibles sur internet, ces images sont très pratiques car certains motifs sont très répandus, et les utilisateurs y sont déjà habitués.
La première étape pour traiter le résultat, c’est de vérifier les éventuelles erreurs. La première vérification est le code de réponse de la requête HTTP, représentée par l’attribut Ajax.status. Le code de réussite, nous l’avons vu plus haut est le code 200.
Il peut être dans votre intérêt d’utiliser les codes d’erreur HTTP si ils peuvent correspondre à l’erreur constatée. Par exemple, renvoyez le code 403 si l’utilisateur à fait appel à une fonction qu’il n’avait pas le droit d’utiliser, vous pouvez par ailleurs transmettre un message personnalisé (en XML ou en texte brut) même si vous renvoyez un code d’erreur. Pensez bien sûr à définir une syntaxe pour vos messages d’erreur, même si elle est simpliste.
Une fois que tout est prêt, la requête peut-être crée et envoyée avec les fonction open() et send(). L’exemple présenté utilise la méthode GET pour l’envoi, c’est pourquoi le paramètre passé à la fonction send() est null
Enfin, le dernier paramètre de la fonction open() permet de choisir si l’envoi sera synchrone. Ici fixé à la valeur false, il est donc asynchrone. Cela signifie que le script n’attend pas le résultat de la requête pour continuer son exécution.
Pratiques de conception
Il existe des pratiques et des méthodes qui ont été étudiées par certaines entreprises et certains développeurs spécialisés. Généralement, il s’agit d’adapter les méthodes déjà existantes au nouvel outil Ajax. Cependant, ces pratiques sont essentielles pour concevoir une application viable. En voici quelques unes.
Pratiques de conception
Il existe des pratiques et des méthodes qui ont été étudiées par certaines entreprises et certains développeurs spécialisés. Généralement, il s’agit d’adapter les méthodes déjà existantes au nouvel outil Ajax. Cependant, ces pratiques sont essentielles pour concevoir une application viable. En voici quelques unes.
Choisissez vos outils
Pour bien travailler, il faut un bon environnement de travail, et donc des bons outils. Dans le cas des applications pour le web, efficace n’est pas toujours lié à coûteux, et de nombreux logiciels gratuits ou libres sont à disposition des développeurs.
Le choix de l’éditeur est essentiel. Si vous avez l’habitude d’utiliser l’éditeur Eclipse pour concevoir vos applications, je vous invite à vous pencher sur Aptana, ce logiciel est également disponible sous forme de plug-in pour votre Eclipse. Aptana intègre de nombreux outils pour les développeurs web et le support de nombreux frameworks. Préférez un éditeur avec lequel vous vous sentez à l’aise et qui soit à la taille de votre projet. Il est inutile de choisir un éditeur trop lourd pour un projet de petite taille, alors qu’un éditeur avec des outils de débbugage avancés et capable d’interagir avec votre serveur web sera particulièrement pratique lorsque votre projet prendra du volume. Quoi qu’il en soit, évitez de changer de logiciel en cours de route, au risque de perdre beaucoup de temps pour vous réadapter.
Le navigateur firefox sera également un outil précieux pendant le processus de développement, notamment grâce à ses nombreuses extensions. La barre d’outil du développeur web vous facilitera en particulier la conception des feuilles de style CSS, et Firebug vous offrira un outil très complet pour faire des tests de vitesse ou manipuler le DOM. L’inspecteur DOM, fourni par les développeurs de Firefox, permet de naviguer dans l’arbre tel que le navigateur l’a interprété. Ces trois extensions sont presque incontournables, mais il en existe bien d’autres.
Enfin, pour programmer dans de bonnes conditions, il est nécessaire d’avoir à portée de main les principales documentations des langages que vous utilisez. Vous avez déjà la documentation PHP (http://fr.php.net/) à portée de main, mais d’autres ressources vous seront utiles, comme sur http://web.developpez.com. Un très bon site regroupe également la liste des balises XHTML 1.1 et les attributs disponibles (http://giminik.developpez.com/xhtml/). Les documents du W3C (www.w3.org) vous seront, quand à eux, utiles pour vérifier des points précis d’une norme.
HTML, XML et l’API Dom
Pour faire évoluer votre page sans la recharger, il est indispensable d’utiliser une API proposée par le W3C appelée DOM (pour Document Object Model pour modèle objet du document). Grâce à DOM, vous avez à disposition d’un bon nombre d’objets et de méthodes vous permettant de modifier le corps d’un document XML. XHTML reposant sur XML, se conformer à cette norme devient également incontournable.
Le DOM organise le document XML en noeuds (node). Un noeud contient un certain nombre d’éléments, les attributs éventuels, un contenu de type texte ou une série d’autres noeuds. Observez bien la manière dont l’arbre est construit par Firefox depuis l’extension Inspecteur DOM, vous remarquerez au passage que les retours à la ligne et les tabulations utilisées pour mettre en forme le code sont considérées comme des éléments texte d’un noeud.
DOM est décliné dans de nombreux langages, dont Javascript et PHP5.
En Javascript, il se manipule à partir de l’objet racine document. On accède aux noeuds fils du noeud courant avec monNoeud.childNodes, par exemple pour accéder à l’élément HTML, il suffit d’utiliser document.childNodes[1], ou encore document.firstNode. D’autres attributs sont disponibles, à savoir monNoeud.lastNode, monNoeud.nextSibling ou monNoeud.previousSibling.
Des méthodes de recherche sont également disponibles, comme monNoeud.getElementsByTagName('div'),
qui retourne un tableau contenant chaque noeud de type div.
D’autres méthodes permettent de créer ou modifier certains noeuds, comme createElement() ou createTextNode(). Vous pouvez ensuite les insérer dans le DOM avec appendChild(), insertBefore(), replaceChild() ou même en supprimer un avec removeChild().
Entraînez vous à naviguer dans le DOM, avec de l’habitude, l’accès à n’importe quel élément de la page sera un jeu d’enfant. L’exemple de code suivant donne un aperçu de la manipulation du DOM en Javascript :
/* Pour manipuler le DOM, la page doit être chargée, donc * on attend l'évènement onload. */ window.onload = function() { // On garde en mémoire le premier paragraphe de la page paragraphe = document.getElementsByTagName("p")[0]; // On construit un nouvel élément titre contenant du texte precision = document.createElement("h1").appendChild(document.createTextNode("Titre de page")); // On injecte cet élément juste avant le premier paragraphe paragraphe.parentNode.insertBefore(precision, paragraphe); }
DOM sera énormément utilisé avec Javascript. Il servira à manipuler les documents XML obtenus par les requêtes Ajax ou implémenter les éléments de l’interface qui la rendent dynamique (boutons supplémentaires, messages de retour, …). DOM sera certainement utile en PHP également. L’implémentation est très semblable à celle de Javascript, certaines méthodes supplémentaires ont été ajoutées, notamment en ce qui concerne la manipulation des fichiers ou la vérification de la conformité d’un document d’après sa DTD. Le listing suivant présente un aperçu de l’implémentation de DOM avec PHP5. L’implémentation varie en PHP4, car les noms de fonctions ont été adaptés à la nomenclature classique de PHP.
$xml = new DOMDocument(); // D'après le listing n° 4 $xml->load('listing4.xml'); // Obtenir les articles $articles = $xml->documentElement->getElementsByTagName('article'); // Titre du premier article $titre = $articles->item(0)->getAttribute('titre');
En PHP on charge un fichier avec la fonction load(). Il est possible de charger un flux dans une variables avec la fonction loadXML(). Pour charger du HTML, il existe deux fonctions spécifiques : loadHTML() ou loadHTMLFile(). Pour enregistrer le résultat save() et saveHTMLFile() s’en chargeront, et pour récupérer le résultat dans une variable, il faudra utiliser saveXML() et saveHTML().
Pour valider un flux XML d’après la DTD, utilisez la fonction validate(), la DTD étant précisée dans la première ligne du fichier (si elle existe), PHP se charge de l’obtenir. Si le format du flux a été définit en utilisant RelaxNG, utilisez relaxNGValidate() ou relaxNGValidateSource() si le schéma est stocké dans une variable.
Les fonctions propres à la navigation ou la modification du DOM sont identiques (à quelques exceptions près) entre PHP5 et Javascript.
La manipulation du DOM avec PHP est moins courante. Pour créer un flux XML, l’API SimpleXML sera généralement plus pratique, bien que moins puissante. DOM peut être utile pour modifier dynamiquement un fichier XML enregistré physiquement, ou téléchargé depuis un serveur distant.
Choisir un framework Javascript
Un choix généralement important à notre niveau est le choix d’un framework Javascript. Il en existe plusieurs sur internet, qui ont tous leurs avantages et inconvénients.
Généralement, ces frameworks sont répartis en plug-ins, il est donc possible de sélectionner les fonctionnalités dont vous avez besoin et éliminer celles dont vous n’avez pas l’utilité. Par ailleurs, certains frameworks seront plus portés sur l’amélioration des possibilités du dom, certains implémenteront aussi un certain nombre d’effets visuels améliorant l’interface alors que d’autres se concentreront sur la communication entre le client et le serveur avec Ajax ou certaines alternatives.
Le choix d’un framework dépend de vos besoins, mais à nouveau, ce choix doit être réalisé assez tôt dans la conception, car vous en serez dépendant.
Voici une petite liste de frameworks courants, c’est à vous de choisir en détail celui qui vous semble le plus approprié à vos besoins. La réactivité de la communauté des utilisateurs et la qualité de la documentation doit également être un critère jouant sur votre choix.
- Y!UI (Yahoo! User Interface) : http://developer.yahoo.com/yui/ Développé et utilisé par les services de Yahoo!, ce framework est fonctionnel et bien documenté. Il reste cependant limité aux besoins internes de la société. C’est un très bon choix pour se limiter à l’essentiel (ou presque).
- Mootools : http://www.mootools.net Ce framework est l’un des plus récents. Son principal avantage est l’amélioration du DOM grâce à des sélecteurs identiques à ceux utilisés en CSS. Il est également réputé pour être assez léger. Cependant, certains outils particulièrement appréciés dans d’autres frameworks sont prévus mais pas encore disponible à ce jour. Il est par ailleurs assez simple d’utilisation, mais la documentation n’est pas très pratique.
- Prototype : http://www.prototypejs.org Prototype est l’un des premiers frameworks de ce type apparus. Particulièrement complet et robuste, il intègre des fonctions Ajax et d’amélioration du DOM puissantes. Un second framework nommé Scriptaculous (http://script.aculo.us) basé sur Prototype dispose de nombreux effets très agréables, mais aussi très lourds.
- jQuery : http://www.jquery.com C’est un framework très réputés dans les projets open source français, il est notamment utilisé par des applications comme Spip ou DotClear.
- D’autres frameworks comme Dojo ou Microsoft Atlas peuvent également être cités.
Une dernière alternative est le GWT (Google Web Toolkit), qui vous permet de programmer la partie cliente de votre application en Java. Le GWT se chargera de le transcrire en Javascript.
Frameworks PHP/Ajax
Généralement, le développement de plusieurs applications web entraîne la programmation de même fonctions plusieurs fois, avec quelques adaptations propres au projet. Cela devient rapidement désagréable car très redondant, et risque d’embrouiller le développeur qui jongle entre plusieurs standards et règles de conception variant d’un projet à l’autre.
C’est pour ces raisons qu’il existe des frameworks complets vous imposent des méthodes de conceptions standardisées. Ces frameworks sont basés sur une architecture stable et des librairies de fonctions ou classes qui lissent l’implémentation des outils classiques comme les bases de données. Leur organisation facilite également le développement à plusieurs grâce à la séparation de l’application en modules indépendants.
En PHP, quelques frameworks sont jugés réellement matures par la communauté. Parmis eux, on trouve le projet français Symfony (http://www.symfony-project.org). Il repose sur les motifs de conception MVC et propose de nombreux outils pour faciliter la conception d’une application ou d’un site internet, comme la virtualisation de la base de données, la simplification de l’internationalisation ou même l’optimisation du référencement, notamment avec l’URL Rewriting. Enfin, Symfony simplifie également l’utilisation d’Ajax.
La répartition client/serveur
Ce sujet à déjà été évoqué ponctuellement plus tôt. La répartition entre le client et le serveur est cruciale pour une application web, la balance doit être équilibrée pour permettre de décharger le serveur de certains traitements, mais l’interface doit rester fluide, d’autant plus que décharger la logique métier vers le client entraîne des risques de sécurité importants.
En local, on s’attarde essentiellement sur l’interface et l’aspect utilisateur. On peut par exemple simplifier la saisie et effectuer une série de tests sur les données entrées par les utilisateurs avant de les transmettre au serveur. Certains calculs peuvent également être traiter en local, pour ne renvoyer au serveur que l’essentiel, cependant, cet aspect est généralement assez limité.
La logique métier est systématiquement sur le serveur, le traitement, la gestion et la redistribution des données se fait invariablement sur le serveur. Par ailleurs, l’envoi d’une requête avec Ajax doit être effectuée sur le serveur hôte de la page courante, pour la sécurité de l’utilisateur.
Cette répartition, comme tout choix de conception, doit être délimitée de manière cohérente. Elle entraîne également de nombreux risques de sécurité. Par exemple, n’oubliez pas que toute donnée transmise doit être contrôlée, car il est toujours facile de détourner le fonctionnement d’un script et de transmettre des informations frauduleuses. Chacune des vérifications effectuées en local doit être reprise sur le serveur. Les failles de sécurité classiques comme les injections SQL risquent d’être plus nombreuses, mais la manière de les éviter est toujours la même : vérifier la cohérence des données et échapper les caractères comme les guillemets utilisés dans les requêtes. Pour finir, les données sensibles doivent toujours être protégées. Par exemple, si certaines données doivent être inaccessibles pour un utilisateur non identifié, les contrôles appliqués lors du chargement d’une page doivent également être effectués avant de transmettre une réponse à une requête Ajax.
Organisation de l’application
A l’image de la partie serveur, la partie cliente de votre application doit également être modélisée et organisée. Il est impératif de veiller à séparer la couche de traitement des données et de communication avec le serveur de la couche graphique, comme la modification du DOM de la page ou les effets visuels.
Le motif de conception MVC peut également être adapté à une architecture cliente conçue avec Ajax. Le contrôleur sera chargé d’analyser les évènements et de déclencher les actions en conséquence, la vue concerne bien sûr l’aspect visuel (la couche “graphique”) et le modèle est chargé de communiquer avec le serveur et de recevoir les données. Le listing qui suit présente un exemple simplifié d’architecture MVC appliquée à Ajax.
/* Nous travaillons avec un formulaire contenant un seul champ * dont l'information est envoyée au serveur avec XMLHttpRequest */ var myController = { start: function() { // Définir les évènements par défaut document.getElementsByTagName('form')[0].onsubmit = myController.submitForm; }, submitForm: function() { input = document.getElementsByTagName('input')[0]; if(input.value == '') myView.displayError(); else { myView.disableForm(); myModel.sendInformation(); } }, endOfQuery: function() { myView.stopLoader(); myView.enableForm(); if(myModel.result != undefined) myView.showResult(); } }; var myView = { dislpayError: function() { element = document.createElement('p').appendChild(document.createTextNode('Champ incomplet'); input = document.getElementsByTagName('input')[0]; input.parentNode.insertBefore(element, input); }, disableForm: function() { document.getElementsByTagName('input')[0].disabled = true; document.getElementsByTagName('input')[0].disabled = true; } enableForm: function() { document.getElementsByTagName('input')[0].disabled = false; document.getElementsByTagName('input')[0].disabled = false; }, queryLoader: function() { // afficher un message d'attente }, stopLoader: function() { // masquer le message d'attente }, showResult: function() { // Traitement du resultat et affichage } }, var myModel = { result: undefined, sendInformation: function() { Ajax = new XMLHttpRequest(); Ajax.open('GET', 'page.php?text=' + document.getElementsByTagName('input')[0].value); Ajax.send(null); }, queryState: function() { if(Ajax.readyState = 4) { if(Ajax.status == 200) { myController.endOfQuery(); this.result = Ajax.responseXML.documentElement; else { myController.endOfQuery(); myController.queryError(); } } else myView.queryLoader(); } }; window.onload = myController.start;
L’organisation de vos fichiers prend également beaucoup d’importance car une organisation souple permet de tirer partie des avantages du cache du navigateur. Généralement, choisir un fichier par classe est une bonne solution.
Optimisations
Comme pour toute application, l’optimisation est ici un point important à ne pas négliger. Une application optimisée signifie un meilleur confort pour l’utilisateur et surtout une économie de ressources sur le(s) serveur(s).
Les appels au serveur sont particulièrement coûteux, dans la mesure du possible, c’est toujours avantageux de les économiser et de les regrouper si possible. Les échanges sur le réseau peuvent aussi être optimisés en réduisant le volume des données échangées. Privilégier des index numériques aux chaînes de caractères dès que possible est déjà un bon point. Dans des cas plus extrêmes, il est parfois intéressant de définir un second format pour les flux XML en réduisant au maximum le nom des attributs et en réduisant les espaces blancs dans le codes. Il existe aussi des outils permettant d’optimiser le code Javascript au maximum. Ces solutions ne doivent être mises en place que lors de l’utilisation de l’application en production, car le code devient généralement illisible et impossible à comprendre.
L’optimisation passe aussi évidemment par la gestion des calculs. Il est donc primordial de limiter les manipulations lourdes du DOM très coûteuses pour le navigateur. Une politique de mise en cache doit aussi être définie sur le serveur et avec le navigateur, grâce aux en-têtes HTTP notamment.
Gardez en mémoire qu’à chaque appel au serveur des opérations qui risquent d’être superflues sont effectuées, comme le rechargement de certains objets voire même de requêtes coûteuses à la base de données.
Enfin, les navigateurs actuels ont généralement des soucis de fuites de mémoire, en particulier lors de l’utilisation de l’API DOM. Une fuite de mémoire est la conséquence d’un maintien en mémoire inutile de certaines variables qui ne sont plus référencées. Une extension pour Firefox appelée Leak Monitor (http://addons.mozilla.org/firefox/2490) très pratique permet de les détecter. Généralement ces problèmes sont dus à des écouteurs d’évènements persistants, il suffit alors la plupart du temps de leur affecter une valeur nulle, par exemple en effectuant monElement.onclick = null; au déchargement de la page. Paradoxalement il faudra donc surcharger l’évènement onunload puis éventuellement lui réaffecter une valeur nulle à la fin de son exécution.
Aspect utilisateur : bonnes pratiques
Il est très important pour bien concevoir une interface de passer par une phase sur papier. N’hésitez pas à prendre du temps pour faire des schémas clairs et annotés car généralement la réalisation des interfaces arrive beaucoup plus tardivement lors du développement. Guider l’utilisateur en organisant intelligemment les différents éléments de l’interface est essentiel afin de la rendre claire et facile à manipuler, c’est pour cela que les effets visuels seront choisis avec parcimonie doivent avoir un sens précis pour le visiteur (comme les couleurs d’ailleurs).
Dans le but de faciliter la communication et la compréhension de votre application, il est conseillé de standardiser les messages et les explications : utiliser les mots login et nom d’utilisateur pour désigner la même information est une mauvaise pratique très répandue ! Veillez par ailleurs à être le plus clair et concis que possible.
Le comportement du navigateur
Tout internaute est habitué à utiliser les fonctions de navigation (“précédent”, “suivant” et “actualiser”) ou les marque-pages, or en transformant la page avec Ajax elles perdent leur comportement normal (la page revient à son état d’origine). Il faut donc pallier à ce problème grâce à certains artifices que nous allons détailler.
Prévoyez des paramètres optionnels dans l’URL de la page qui permettront de retrouver l’état intermédiaire de la page. Imaginez une page où le texte varie en fonction d’une liste déroulante, il faudra ajouter un paramètre text contenant l’identifiant du paragraphe que l’utilisateur souhaite retrouver. On affichera alors un lien intitulé “Lien direct vers cette page”, par exemple, qui évoluera à chaque changement de liste déroulante (par exemple page.php?text=2). Dans le code, on testera la présence où non de ce paramètre : si il existe, on modifiera la valeur par défaut de la liste déroulante et le texte affiché, sinon, on laissera la page se charger normalement. Cette solution ne permet cependant pas de tout résoudre, et un choix judicieux entre le rechargement complet de la page ou une requête avec Ajax permet de limiter les dégâts.
Accessibilité
Ajax est les applications riches sont généralement sources de problèmes d’accessibilité. Plusieurs règles sont à adopter pour permettre à un maximum de visiteurs de pouvoir consulter le site conçu.
N’hésitez pas à préciser du contenu invisible destiné aux lecteurs d’écran, par exemple, placez une balise <span class="accessible">Début/Fin de listing de code PHP</span><br /> aux deux extrémités d’un listing de code, ou d’autres éléments habituellement repérables par une modification de la présentation. Il vous suffira de préciser que les éléments "accessible" ne doivent pas être affichés sur une sortie classique depuis les feuilles CSS. Pensez aussi à mettre quelques raccourcis en haut de
page, menant directement au menu, à la recherche et au contenu du site : les utilisateurs de navigateurs mobiles apprécieront. Pour finir, lorsque vous utilisez des formulaires dans vos applications, utilisez les balises d’organisation telles que <fieldset>,
<legend> ou encore <label>, et les attributs comme accesskey pour proposer des raccourcis claviers à l’utilisateur. Le listing suivant montre un exemple d’utilisation de ces balises.
Une pratique essentielle pour permettre l’accessibilité d’une application Ajax est appelée dégradation élégante (ou graceful degradation). Elle consiste à toujours mettre en place une alternative cohérente et facile d’utilisation utilisant le minimum (voire aucune) technologie qui risque de ne pas être exploitable par certains utilisateurs.
Un exemple simple et courant de dégradation élégante est le code utilisé pour afficher une page dans une nouvelle fenêtre (depuis que l’attribut “target” est devenu obsolète en HTML) : <a href="mapage.php"
onclick="window.open(this.href); return false;">Lien</a>. Ici, Javascript détourne le rôle du navigateur pour traiter lui
même l’évènement en ouvrant le lien voulu dans une nouvelle fenêtre, il annule l’action normale de chargement de page grâce à
return false;. Si l’utilisateur n’active pas Javascript, la page se charge tout de même normalement.
Cette pratique est au coeur d’une méthode de conception des applications riches proposée par Jeremy Keith (http://domscripting.com/blog/display/41) il y a deux ans : Hijax. Il s’agit de réfléchir à la manière d’appréhender l’implémentation d’Ajax et dans son application. On peut la résumer grossièrement en disant qu’il faut savoir dès les premières réflexions sur l’architecture de l’application qu’Ajax sera utilisé, mais la concevoir et la faire fonctionner sans l’implémenter dans un premier temps. On ajoute alors les fonctions de l’interface dynamique en surcouche. On parle alors plutôt d’amélioration progressive (progressive enhencement).
Conclusion
Vous avez maintenant en main un bon nombre de pistes à approfondir pour concevoir des applications riches puissantes et efficaces. Gardez en tête que l’utilisation d’Ajax doit toujours être justifiée car c’est un niveau de complexité supplémentaire et peut poser des problème à certains utilisateurs. A présent, soyez créatifs et inventifs, une nouvelle approche du web est à votre portée !
Pour en savoir plus :
- Traduction de la documentation de DOM dans le cadre des projets Mozilla,
- Documentation Ajax dans le cadre des projets de Mozilla,
- Le journal du net vous propose de nombreuses ressources de bonne qualité sur le développement d’applications riches,
Je vous conseille également l’excellent livre Développer pour le Web 2.0 (Eric Van der Vlist, Danny Ayers, Erik Bruchez, Joe Fawcett, Alessandro Vernet) aux édition First Interactive - qui traite de nombreuses technologies et méthodes pour concevoir des applications Web 2.0 très puissantes.