• Recherchez sur css4design

Utilisez le DOM et Javascript comme un chef

dom-javascript Comment remplacer les gestionnaires d’événements présents dans une page web en redéfinissant le comportement onclick, onmouseover… des éléments XHTML ? Tout simplement en utilisant la magie du DOM et de Javascript. Généralement, lorsqu’on veut appliquer une fonction sur un élément XHTML (ouvrir un popup lorsqu’on clique sur une image, par exemple), on utilise une fonction popup() déclenchée par un événement onclick placé dans la balise comme ici : <img src=”/img1.gif” onclick=”popup()” />. C’est parait simple, efficace et rapide… Or, ça peut vite devenir compliqué, inefficace et lent…

Comment ça, il est pas frais mon gestionnaire ?

En effet, il faut ajouter systématiquement le gestionnaire d’événement pour chaque élément concerné. Ce qui rend le code moins lisible. Ce qui augmente aussi les risques d’erreurs de saisie ou de copié-collé qui prennent toujours plus de temps qu’on l’imagine à résoudre.

Mais surtout, cette façon de procéder n’entre pas dans la logique d’une saine séparation entre le contenu (texte, image, multimédia), encadré rigoureusement par nos balises XHTML, gardiennes du temple sémantique ! Balises, que l’on prendra soin de styler avec CSS, de préférence dans un fichier séparé.

Alors, si vous pensiez mettre des événements onclick, onmouseover ou onchange en veux-tu en voilà, vous avez tout faux… En ce qui me concerne, c’est surtout depuis le jour où Thanh m’a regardé d’un drôle d’air quand je lui ai montré mon… gestionnaire onclick, que je me suis remis en question ;)

Redéfinissez-moi tout ça

A la place de ce gestionnaire d’événement chronophage et pas très Web 2.0, je vous propose un exemple qui nous permettra de redéfinir le comportement des balises img se trouvant à l’intérieur d’une div images :

function defineEventInContainer(container,element) {
    var strContainer = document.getElementById(container);
    var arrElements = strContainer.getElementsByTagName(element);
    var intElements = arrElements.length;
    for (var i=0; i<intElements; i++) {
        arrElements[i].onclick = toto;
    }
 }
function toto() {
    alert("Fonction déclenchée sur les balises img contenues dans le paragraphe images");
}
<body onload="defineEventInContainer('images','img');">

Cette fonction attend deux arguments : le premier contient un id qui servira à délimiter la portée de notre fonction (le container ou bloc parent), et le deuxième contient la balise dont on veut modifier le comportement.

Ensuite, on affecte le premier argument à la variable strContainer. La ligne suivante parcours la portion du document à la recherche des éléments spécifiés, et empile le résultat dans un tableau que l’on affecte à la variable arrElements. A ce stade, nous avons un array comportant tous les éléments img contenus dans la div images.

Nous comptons ensuite le nombre d’items contenus dans arrElements, ce qui nous permet de faire une boucle pour parcourir les images une à une tout en leur affectant la fonction toto().

N’oublions pas que cette fonction doit être présente dès le chargement de la page : un coup de onload dans le body avec les arguments qui vont bien, and the cat’s in the bag!

Je suis d’accord avec vous, c’était bien la peine de mettre autant de neurones sur l’affaire pour finir avec un pauvre alert(). Mais je suis sûr que vous trouverez mieux à mettre sous la dent de notre bon toto().

Appliquer une fonction selon la classe CSS »

This entry was posted in Javascript & PHP and tagged , , , , , . Bookmark the permalink. Trackbacks are closed, but you can post a comment.

25 Comments

  1. Posted 05/10/2006 at 09:54 | Permalink

    Effectivement très utile !

    Et maintenant, pour éviter le <body onload…> tu as aussi

    try{window.onload=function(){loadDivs();}}
    catch(e){window.attachEvent("onload", loadDivs);}

    On peut aussi imaginer passer le nom de la function à exécuter en paramètre de defineEventByClass…

    Rhaaa la la c’est bien inspirant tout ça !

  2. Posted 05/10/2006 at 09:56 | Permalink

    J’oubliais… "loadDivs" étant une function dans laquelle on regroupe tout ce qui se lance au chargement de la page, c’est un exemple, pas quelque chose qui existe, hein /o\

  3. Posted 05/10/2006 at 10:17 | Permalink

    Tout ça pour Toto … Quel veinard ^^

  4. Posted 05/10/2006 at 10:48 | Permalink

    @Talou -> Merci pour le try… catch. Dans loadDivs() je mets quoi exactement ? Des fois, faut m’expliquer comme si j’avais 5 ans…
    @xuxu -> Toto insistait, alors… Au fait, sympa les bannières Copaing.net ^^ Ca me donne une idée ;)

  5. Posted 05/10/2006 at 16:14 | Permalink

    ça parle de quoi ici?

  6. Posted 05/10/2006 at 16:26 | Permalink

    @thanh -> Si aussi tu codais un peu plus, au lieu de serrer des mains et de distribuer des cartes… :p

  7. Posted 05/10/2006 at 16:28 | Permalink

    Ben justement j’avais vu de la lumière j’ai cru qu’on servait à boire!

  8. Posted 05/10/2006 at 16:57 | Permalink

    Vivement le vin, donc ;)

  9. Posted 05/10/2006 at 21:25 | Permalink

    Encore plus fort ! Si tu veux supprimer le javascript de ton doc html, tu peux carrément utiliser la fonction addevent de Scott Andrew (http://www.scottandrew.com/weblo...

    function addEvent(obj, evType, fn){
    if (obj.addEventListener) {
    obj.addEventListener(evType, fn, true);
    return true;
    } else if (obj.attachEvent) {
    var r = obj.attachEvent("on"+evType, fn);
    return r;
    } else {
    return false;
    }
    }

    où obj est ton element html,
    evType, ton type d’évènement (click, over, out…)
    et fn, la fonction que tu appelles.

    Par exemple pour initier un script dans un doc html, tu peux faire
    addEvent(window, ‘load’, maFonction);

    et là, c’est vraiment Magic !

  10. Posted 05/10/2006 at 22:28 | Permalink

    @Gregoire -> Je connaissais pas du tout cette méthode addEventListener qui a l’air aussi puissante que subtile. Je teste dès que possible !

    P.S. Le web c’est génial : c’est quand tu crois avoir compris un truc, que tu découvres des choses vraiment intéressantes ;)

  11. Posted 05/10/2006 at 23:55 | Permalink

    Et si vraiment t’as pas compris, reprend un verre!

  12. Posted 06/10/2006 at 00:14 | Permalink

    lol, pour ça, je crois que je ferais mieux d’attendre l’apéro-blog du 20 :)

  13. Posted 06/10/2006 at 00:34 | Permalink

    br1o : j’ai mis une function qui s’appelle loadDivs, ok, mais elle peut s’appeler autrement, et dedans tu mets toutes les fonctions que tu veux charger au démarrage… Admettons que tu aies besoin d’attacher une action à tous les éléments de class XX et et une autre action à tous les éléments de class YY, eh bien tu as une fonction

    function start()
    {
    defineEventByClass("img_foo", "f_foo");
    defineEventByClass("div_bar", "f_bar");
    }

    en admettant que la fonction defineEventByClass permette de définir en paramètres le nom de class et le nom de la fonction à appeler dans ce cas…

    Ca marche aussi avec l’astuce encore plus élégante de Grégoire.

  14. Bewonder
    Posted 22/10/2006 at 17:49 | Permalink

    Merci pour ces informations, l’astuce de Gregoire étant trés "élégante".

    Par contre, je me heurte à un problème… le passage de paramètres.
    En gros, j’ai un système de navigation par boutons images.
    Chaque bouton doit valider le formulaire de la page actuelle avant d’être redirigé.
    Je voudrais que ma fonction "toto" récupère des informations fixées pour chaque lien (appel de la nouvelle page) et permette la validation du formulaire.

    J’ai eu l’idée de coller ces paramètres (du style "11" pour la page 1-1 ou "12" pour la page 1-2…) dans le paramètre ID de chaque lien.
    Mais impossible de récupérer la valeur de l’id du lien cliqué.

    ex :
    <div id="images">
    <a id="11"…><img…></a>
    <a id="12"…><img…></a>

    </div>

    et l’appel de la fonction : onload="addEvent(images, ‘click’, toto, this.id);"

    Mais ça ne marche pas, this.id ne semble pas être possible d’obtenir comme ça.

    Merci pour vos commentaires.

  15. Bewonder
    Posted 23/10/2006 at 15:51 | Permalink

    Bon, comme je dois me débrouiller seul…
    Voilà ce que j’ai fait :

    Dans chaque formulaire (dans chaque page donc) je créé deux champs cachés (page et etape).
    Sur mes liens, je créé un évenement onMouseOver qui renseigne ces valeurs à chaque passage de la souris.
    J’ai repris la première fonction de br10, toto renseigne la valeur document.forms['form1'].action avec l’état actuel de chaque champ caché puis poste le formulaire.

    Voilà, ce n’est peut être pas trés élégant mais ça marche.

  16. Posted 23/10/2006 at 17:57 | Permalink

    Bonjour Bewonder,
    Quand je n’ai pas la réponse à une question, je laisse passer un peu de temps au cas où quelqu’un d’autre aurait une solution. Concernant la façon dont tu as résolu ton problème, je n’ai pas mieux à proposer ;) Well done !

  17. irongomme
    Posted 22/11/2006 at 16:48 | Permalink

    Bonjour à tous,

    C’est fort sympathique de pouvoir redefinir les evenement mais je suis confronté au meme probleme que bewonder, c’est a dire que si je reprend l’exemple ici, il faudrait que ma methode toto puisse avoir acces au moins à l’instance de l’objet qui la declenche … y a t’il un moyen de pouvoir faire ca ?

    ou peut t’on tout simplement envoyer des parametres dans la methode toto ?

    merci.

  18. irongomme
    Posted 22/11/2006 at 20:28 | Permalink

    J’ai trouvé la solution !!

    exemple :

    document.getElementById(’machin’).onclick = function() { toto(argument1, argument2, etc…); };

    Ca peut être utile pour ceux qui devellope à la façon AJAX sans framework ! On peut élaborer de véritables applications en couplant ça avec du php.

    Tchao

  19. Posted 22/11/2006 at 20:50 | Permalink

    Bravo et merci de la part de tout ceux qui bloquaient dessus ;)

  20. yafa
    Posted 02/01/2007 at 23:01 | Permalink

    bonjour, es que vous pouvez m’indiquer des docs important concernant le dom..
    merci

  21. br1o
    Posted 03/01/2007 at 07:50 | Permalink

    Bonjour yafa, concernant le dom, voici quelques liens supplémentaires :
    slayeroffice.com/articles…
    nyams.planbweb.com/dom.ht…
    gilles.chagnon.free.fr/co…

  22. Posted 25/06/2008 at 13:35 | Permalink

    Salut Bruno! Décidément j’arrive toujours a me retrouver chez toi :D juste pour te signaler que les sources JS ne passent pas (comme tu es en pleine refonte de design je prefere te l’dire ;-) )

    A bientôt!

    Antoine

  23. Bruno Bichet
    Posted 11/08/2008 at 22:31 | Permalink

    @guiralantoine > yep, je corrige ça peu à peu quand je tombe sur les billets qui comportent des exemples de code. Avec plus de 230 billets en tout, ça prend du temps… Désolé ;)

  24. Posted 20/08/2008 at 02:17 | Permalink

    J’avoue que pour ma part, je préfère de loin faire confiance à un framework éprouvé comme jQuery qui permet en une ligne d’associer des fonctions d’événement à tous les éléments DOM que l’on souhaite. Bref, c’est un gain de temps des plus intéressants, quand on ne souhaite pas s’investir dans une R&D importante et que l’on est intéressé plus par le résultat que la manière.

    D’autres préféreront Prototype, voire des choses plus complètes comme Scriptaculous et autres MooTools.

  25. Bruno Bichet
    Posted 20/08/2008 at 02:45 | Permalink

    @Martin: A l’époque je ne connaissais pas jQuery et je ne sais même pas si ça existait ;) Depuis que j’ai découvert ce framework jQuery, je ne peux plus m’en passer.

    N’empêche que quand on n’est pas développeur, c’est quand même intéressant de connaitre les différentes strates qui nous ont amenés des événement onclick dans la balise aux comportement non obstrusifs…

    Je m’étais bien grillé quelques neurones pour comprendre et mettre en place la solution présentée dans ce billet ^^

    PS pour tous : comme j’ai rajouté Markdown Extra, j’ai plein de pétouilles à régler dans l’affichage des billets. J’y travaille ;)

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe without commenting