Aller au contenu

Javascript : séparer comportement et structure


pierredureau

Sujets conseillés

Dans mon nouveau site, je compte utiliser ce menu de Raphaël GOETTER :

http://www.alsacreations.com/articles/modelesmenus/hd1.htm

Mais j'ai eu le malheur de lire depuis cet article de Peter-Paul Koch :

http://pompage.net/pompe/separation/

Désormais, je veux que mon menu soit lui aussi épuré des OnMouseOver et OnMouseOut, pour n' "avoir qu'à inclure un script et à ajouter une class ou un id à un ou plusieurs éléments XHTML, que le script interprèterait comme : applique tel comportement ici."

Pouvez vous m'aider à reformuler le menu de Alsacréation pour le rendre "conforme" à ces exigences ? merci d'avance.

Lien vers le commentaire
Partager sur d’autres sites

Salut pierre,

Ce n'est pas très difficile mais cela necessite que tu manipules un peut Ecmascript et le DOM et que tu utilise un outil comme le DOM inspector de Mozilla pour tester l'arbre de la page.

Dans ton cas la séparation comportement/structure pour reprendre la terminologie de Peter implique de remplacer les events sur les dt du menu de raphael par un gestionnaire d'evenement, qui sera mis en place par une fonction d'initialisation invoquée à l'ouverture de la page sur le onload.

D'autres parts tu devras acceder aux éléments de manière anonyme en parcourant l'arbre du DOM, aussi bien lors de l'initialisation que lors du switch de la classe CSS des sous-menus.

Dans cette perspective, les ID des sous-menus ne servent plus à rien, car on peut accéder au sous-menus à afficher en prenant comme position de référence l'element dt surveillé par le gestionnaire d'evenement et en adressant son premier enfant ou l'élement de sa descendance, identifié comme l'élement à afficher.

Donc tu peux virer tous les id des sous-menus...

Dans la technique du roll-over l'affichage du sous-menu ne pose pas de problème, par contre afin d'eviter une boucle qui va traiter tous les éléments à chaque evenement, comme dans le dispositif de raphael, ce qui n'est pas très économique, tu vas utiliser une astuce en affectant à un element "affiché" un id provisoire qui sera utilisé par la fonction de traitement de l'evenement.

Voici, globalement le scenario de ton script, que tu devras mettre dans un fichier externe pour débarasser completement ta page de tout notion de script .

Ce scenario est théorique, je n'ai pas eu le temps de le tester mais le script final devrait y ressembler fortement.

Pour la fonction d'initialisation :

1. Detecter IE pour invoquer la syntaxe propriétaire du browser redmondien concernant les gestionnaire d'evenement.

2. Capturer le tableaux des éléments dt de la page avec document.getElementsByTagName("dt") .

3. A l'aide d'une boucle, parcourir chaque element du tableau en testant l'attribut id du parent de son parent (la structure nodale est : div id='menu' -> dl -> dt ou dt est l'enfant à identifier du parent dl du parent div), les éléments qui t'intéressent sont donc ceux dont le parent du parent est l'élément div ayant valeur d'id "menu"

Tu fais le test avec l'adresse: document.getElementsByTagName("dt").parentNode.parentNode.getAttribute('id')="menu".

4. Pour chaque element identifié, affecter le gestionnaire d'evenement en utilisant les deux syntaxes :

- Pour Gecko : document.getElementsByTagName("dt").addEventListener("evenement", fonction, true)

- Pour IE :

eval(document.getElementsByTagName("dt").evenement=fonction)

note : l'affectation à la fonction est une affectation d'objet donc tu ne met pas d'accolade au nom de la fonction passé au gestionnaire d'evenement ;)

Chaque element dt correctement adressé va donc être surveillé en relation avec l'évenement onmouseover.

Il te suffit maintenant d'ecrire la foonction qui sera invoquée lors de l'evenement surveillé sur l'element identifié, cette fonction devra switcher la classe CSS de l'enfant de l'élément surveillé pour l'afficher, créer un attribut id provisoire servant à identifier "le dernier sous-menus affiché", switcher la classe CSS de ce "dernier sous-menu affiché" pour le replier, détruire l'attribut id provisoire et le réaffecter à l'élément affiché courant.

L'appel de la fonction se fait avec fonction(this.firstChild) ou fonction(this.childNodes[n]) ou [n] est l'index de l'élément enfant à déplier dans la descendance renvoyée par le DOM inspector.

L'ordre de traitement à une grande importance et devra être :

1. Tester si il existe un élément ayant un id provisoire avec document.GetElementById("ouvert")

2. Si oui, switcher la classe CSS de cet élément pour le replier et effacer la valeur de l'attribut id provisoire avec document.GetElementById('ouvert').removeAttribute('id')

3. Switcher la classe CSS de l'élément passé en paramètre de la fonction par le egstionnaire d'évenement, donc de l'enfant dd de l'élément dt sur lequel à lieu l'evenement courant.

4. Affecter un attribut id provisoire "ouvert" avec this.firstChild.setAttribute('id','ouvert') ou this.childNodes[n].setAttribute('id','ouvert')

Prendre un peut de lexomil et tester...

Ta page équipée de ce dispositif sera, coformément au developpement ecmascript "standard", completement debarassé de toute notion de script ecmascript et tes roll-over seront inoxydable.

Par contre, comme tu peux le constater ca demande un peut d'investisement en temps de développement ;)

L'autre intérêt de cette méthode est que pour créer un nouveau sous-menu, il te suffit de rajouter un bloc dl->dt->dd->ul sans autre formalités, car en réalité c'est la structure de ta liste qui va piloter le script et non l'inverse comme dans l'exemple de raphael.

Tu peux même envisager de rendre le traitement itératif pour prendre en charge plusieurs structure de menu/sous menus, en utilisant des id de menu du genre "menu1", "menu2" ect

Enfin, en terme de charge de traitement le recours au DOM est très économique ce qui peut représenter un gain de temps de chargement non négligeable pour de grandes listes.

Si tu coinces, ou si tu n'à rien compris (t'inquiete pas si tu es novice dans le developpement Ecmascript/DOM c'est normal), dis le moi, je trouverais bien une heure ou deux pour écrire le script.

En passant et pour être parfaitement econome, tu pourrais tout aussi bien traiter le roll-over avec une classe CSS qui focntionnera parfaitement bien sur Gecko et réserver le traitement ecmascript à notre ami IE.

La tu serais pas mal : méthode standard quand c'est possible et cautere sur une jambe de bois, mais sans pollution scriptée, pour les afficionados de l'ogre monopolistique de redmond... :) :)

JP

Lien vers le commentaire
Partager sur d’autres sites

Ouaouh ! Et bien, même dans mes rêves, je ne m'attendais pas à une réponse aussi chouette que celle-ci.

Comme tu t'y attendais, je n'ai pas compris grand chose, enfin un petit peu quand même parceque je suis un ancien adepte du JScript microsoftien (j'ai abandonné la conception de scripts lors de ma découverte des standards).

Le dernier point que tu aborde me semble le plus intéressant. Traiter le rollover en CSS pour Gecko, je pense qu'il n'y aura pas de problème pour moi. Mais le DOM continue à m'effrayer.

Tu t'es proposé pour écrire le script. En effet, ça me rendrai un grand service que tu le fasses (tu dis que ça te prendra une heure, c'est beaucoup de temps consacré, mais moi il me faudra une journée). Mais bon, pas que pour moi, c'est le genre de travail qu'il serait utile de publier. Pëut-etre que Raphael voudra mettre à jour son tutoriel...

Lien vers le commentaire
Partager sur d’autres sites

Resalut pierre

J'ai testé la solution que je te donnais dans mon message précédent ce matin au petit dej.

Malheureusement, je ne parviens pas à initialiser correctement le gestionnaire d'evenement sur l'élément dt... :(

J'ai donc modifié l'approche du probleme en prenant comme objectif une méthode plus "classique" qui consiste à créer l'attribut onmouseover à postériori lors de l'initialisation de la page en utilisant le même scenario :

Récupérer le tableau des éléments dt, créer l'attribut onmouseover sur les éléments identifiés qui invoque une fonction afficher basée sur l'enfant à déplier et la mise à jour d'un attribut id provisoire pour replier le "dernier sous-menu affiché".

J'ai une solution qui fonctionne parfaitement sur Gecko mais, évidemment, le plus grand bricolage logiciel depuis l'invention de tim, à savoir IE, resiste impertubablement à la méthode standard ecmascript/dom de création et d'initialisation d'attribut. :evil:

Je te fais signe dés que j'ai trouvé la solution pour dompter IE.

JP

PS : si un expert traine dans le coin pourrait-il me dire où se situe le problème ou la niaiserie de code dans la séquence suivante :

Je ne reçois ni erreur ni exception de l'interpréteur javascript mais le dispositif reste totalement silencieux...

// Detection sommaire de IE
(this.navigator.userAgent.toLowerCase().indexOf("msie")==-1) ? DOM=1 : DOM=null;
function init(){
 //Test de support DOM
 if(document.getElementsByTagName("dt") && document.getElementById){
   for(i=0;i<document.getElementsByTagName("dt").length;i++){
     if(document.getElementsByTagName("dt")[i].parentNode.parentNode.getAttribute("id")=="menu"){
       //Mise en place du gestionnaire d'évènement
      if(DOM){
   document.getElementsByTagName("li")[i].addEventListener("onmouseover", afficher, true);
    }
    else{
   eval(document.getElementsByTagName("li")[i].onmouseover=afficher);
    }
   }
  }
 }
}

Modifié par jpv
Lien vers le commentaire
Partager sur d’autres sites

Reresalut pierre

Bon ben finalement, j'ai trouvé les niaiseries de mon premier essai, donc voici le code qui va te permettre d'atteindre ton objectif : plus de javascript dans la page :) :)

// Detection sommaire de IE
(this.navigator.userAgent.toLowerCase().indexOf("msie")==-1) ? DOM=1 : DOM=null;
  function init() {
   //Si on a le support du DOM requis
   if(document.getElementsByTagName("dt") && document.getElementById){
     for(i=0;i<document.getElementsByTagName("dt").length;i++){
       //Si le grand-pere est bien le menu :=)
       if(document.getElementsByTagName("dt")[i].parentNode.parentNode.getAttribute("id")=="menu"){
         //Affectation du gestionnaire d'evenement
         if(DOM){
           document.getElementsByTagName("dt")[i].addEventListener("mouseover",afficher,true);
         }
         //(Note : envoyer un fax a bilou pour lui parler du support des standards...)
         else{
           document.getElementsByTagName("dt")[i].onmouseover=afficher;
         }
       }
     }
   }
  }
function afficher(){
 //Note: envoyer un fax a bilou...
 (DOM) ? indexN=3 : indexN=1;
 //Si on a un element depliable (pour faire la distinction entre les titres et les liens
 if(cible=this.parentNode.childNodes[indexN]){
   //Si on en à un déjà ouvert on le ferme
   if(document.getElementById("ouvert")){
     document.getElementById("ouvert").style.display="none";
     document.getElementById("ouvert").removeAttribute("id");
   }
   //On ouvre le sous-menus courant
   cible.style.display="block";
   cible.setAttribute("id","ouvert");
 }
}

Quelques commentaires:

La fonction init() s'implémente sur le onload du body.

Tu peux virer tous les id des dt, ils ne servent plus à rien

Tu dois absolument respecter l'ecriture des listes, du point de vue du DOM les retours chariots du code HTML sont des nodes valides, donc un en plus ou un en moins peuvent planter le dispositif.

Comme tu le remarqueras il y à une différence d'interprétation entre IE et Gecko sur l'interprétation de l'arborescence du DOM, qui doit d'ailleurs être en relation avec les fameux retours charios du code HTML.

D'ou la nécessité pour la focntion afficher de modifier la valeur de l'index de l'enfant recherché.

A vrai dire je ne sais pas qui à raison sur le coup Gecko ou IE.

Enfin, ce dispositif ne fonctionne qu'avec les browsers implémentant DOM 2, nottament pour le support de getElementsByTagname qui n'est pas une spécification du DOM HTML mais est une implémentation sauvage d'une propriété du DOM XML, (on embrasse les developpeurs sur les deux joues...)

Donc, maintenant que ta page est equipée d'un joli dispositif, te reste à trouver moyen d'offrir une alternative aux browsers ne pouvant pas supporter le dispositif.

La solution passe par l'implémentation, par défaut de tes sous-menus dans leur états dépliés et de les refermer à l'ouverture de la page par la fonction init.

Il te suffit d'inverser la valeur de classe CSS concernant les dd à ouvrir et de profiter de la boucle d'intialisation pour les fermer en adressant le troisieme (gecko) enfant du parent ou le premier (ie);

Le code:

// Detection sommaire de IE
(this.navigator.userAgent.toLowerCase().indexOf("msie")==-1) ? DOM=1 : DOM=null;
  function init() {
   //Si on a le support du DOM requis
   if(document.getElementsByTagName("dt") && document.getElementById){
     for(i=0;i<document.getElementsByTagName("dt").length;i++){
       //Si le grand-pere est bien le menu :=)
       if(document.getElementsByTagName("dt")[i].parentNode.parentNode.getAttribute("id")=="menu"){
         //Affectation du gestionnaire d'evenement
         if(DOM){
           document.getElementsByTagName("dt")[i].addEventListener("mouseover",afficher,true);
           //Fermeture du sous-menus
           if(cible=document.getElementsByTagName("dt")[i].parentNode.childNodes[3])cible.style.display="none";
         }
         //(Note : envoyer un fax a bilou pour lui parler du support des standards...)
         else{
           document.getElementsByTagName("dt")[i].onmouseover=afficher;
           if(cible=document.getElementsByTagName("dt")[i].parentNode.childNodes[1])cible.style.display="none";
         }
       }
     }
   }
  }
function afficher(){
 //Note: envoyer un fax a bilou...
 (DOM) ? indexN=3 : indexN=1;
 //Si on a un element depliable (pour faire la distinction entre les titres et les liens
 if(cible=this.parentNode.childNodes[indexN]){
   //Si on en à un déjà ouvert on le ferme
   if(document.getElementById("ouvert")){
     document.getElementById("ouvert").style.display="none";
     document.getElementById("ouvert").removeAttribute("id");
   }
   //On ouvre le sous-menus courant
   cible.style.display="block";
   cible.setAttribute("id","ouvert");
 }
}

Et te voila avec un menu "tous temps", déplié, utilisable et accessible pour les vieux browsers, les lecteurs d'écrans ect, plié et utilisable pour les browsers DOM 2.

Elle est pas belle la vie ? ;)

Note : C'est testé sur IE 6+ et Gecko, si tu à des difficultés d'implémentation contacte moi...

Lien vers le commentaire
Partager sur d’autres sites

Ha oui j'oubliais :

Je ne suis pas certain que ce soit vraiment une bonne iddée (quoi que) de proposer à raphael de modifier son script.

En passant, immense respect pour son excellent boulot sur alsacreation ;)...

La raison est que tant que perdurera l'Infame Essai de redmond sur les browsers (je me moque), il est très difficile de généraliser ce genre de méthode...

Il y à autant de différence sur le support du DOM entre Gecko et IE que sur les CSS pour te donner une idée.

Donc l'implémentation de ce genre de dispositif necessite des structures HTML consolidée, par exemple dans notre cas en ce qui concerne les retours chariots du code HTML.

Du coup le pauvre risque d'être inondé de messages et de demande d'aide parceque un zélé webmestre aura voulu supprimer un retour chariot ou modifier l'arborescence du motif primaire du menu.

Mais bon pourquoi pas...

JP

PS : en y repensant il existe bien une solution pour etre sur de tomber sur le bon enfant en utilisant une boucle de detection fondé sur la valeur de classe CSS (il faudrait donc utiliser des classes CSS au lieu de l'héritage utilisé par raphael) de l'element à déplier, je regarderais ça ce soir... :)

Lien vers le commentaire
Partager sur d’autres sites

[re]{3} Pierre

Comme promis voici une derniere (?) release du script :

Comme je l'imaginais il est effectivement possible d'utiliser une boucle de detection pour identifier avec certitude l'element dd à deplier.

Du coup grosse simplification puisque on à plus à tester le browser pour plier/replier les sous-menus.

La seule necéssité de structure HTML est que l'élément DD à replier doit être le prochain élément de ce type dans le motif primaire : dl->dt->dd.

Par contre rien n'interdis d'intercaler d'autres éléments entre le dl et le dt ;)

D'autres part, c'était une chose qui m'éttonnais, dans l'exemple de raphael, le dispositif laissait le menu "ouvert" lorsque le focus était perdu sur l'event mouseout.

Cela ne me semblait pas très logique eut égard au comportement habituel d'un rollover, quoique cela puisse représenter un interêt ergonomique.

Bref, j'ai donc modifier le dispositif en rajoutant un gestionnaire d'évenement sur l'event onmouseout pour simuler parfaitement le roll-over et en transformant la fonction afficher en switch pur.

Enfin j'ai débarrassé le script de l'astuce de l'id provisoire qui ne se justifie plus si on implémente un event mouseout.

Le résultat est un script conforme, beaucoup plus souple concernant les écarts de structure HTML et indépendant de l'interprétation du DOM en ce qui concerne le traitement des events.

Si le scripting client est ok et le browser compatible DOM 2, le menu apparait plié et focntionne parfaitement, dans le cas inverse, rien ne se pase et le menu apparait déplié...

Du coup raphael si tu me lis, regarde le script , je suis sur que ca te donner plein de bonnes idées :) (et si t'a besoin d'un coup de main hésite pas...).

Donc le code :

// Detection sommaire de IE
(this.navigator.userAgent.toLowerCase().indexOf("msie")==-1) ? DOM=1 : DOM=null;
function init() {
var cible=0;
//Si on a le support du DOM requis
if(document.getElementsByTagName("dt") && document.getElementById){
  for(i=0;i<document.getElementsByTagName("dt").length;i++){
    //Si le grand-pere est bien le menu :=)
    if(document.getElementsByTagName("dt")[i].parentNode.parentNode.getAttribute("id")=="menu"){
      //Affectation du gestionnaire d'evenement
      if(DOM){
        document.getElementsByTagName("dt")[i].addEventListener("mouseover",afficher,true);
        document.getElementsByTagName("dt")[i].addEventListener("mouseout",afficher,true);
      }
      //(Note : envoyer un fax a bilou pour lui parler du support des standards...)
      else{
        document.getElementsByTagName("dt")[i].onmouseover=afficher;
        document.getElementsByTagName("dt")[i].onmouseout=afficher;
      }
      //Fermeture du sous-menus
     //Pour tous les enfants du parent de l'élément dt identifié               for(z=0;z<document.getElementsByTagName("dt")[i].parentNode.childNodes.length;z++){
     //L'élément à replier est le prochain élément dd de la descendance           if(document.getElementsByTagName("dt")[i].parentNode.childNodes[z].nodeName=="DD" && cible==0)
         cible=document.getElementsByTagName("dt")[i].parentNode.childNodes[z];
       }
       //On replie
       if(cible){
         cible.style.display="none";
         cible=0;
       }
    }
  }
}
}
function afficher(){
var cible;
//L'element à switcher est le prochain element DD du parent de l'élément dt sur lequel on a l'event
for(i=0;i<this.parentNode.childNodes.length;i++){
  if(this.parentNode.childNodes[i].nodeName=="DD" && !cible)cible=this.parentNode.childNodes[i];
}
//On switch (la premiere condition corrige un affichage d'erreur indue d'IE)
if(cible){
(cible.style.display=="block")? cible.style.display="none" : cible.style.display="block";
}
}

Et yop la boum...

JP

PS: Il y à moyen d'optimiser encore le code, voire de le généraliser mais je verrais ca plus tard....

Modifié par jpv
Lien vers le commentaire
Partager sur d’autres sites

Salut,

Chez moi, sous Firefox 0.9.3, les sous menus apparaissent bien quand je passe le

curseur sur les éléments DT mais disparaissent dés que je ne pointe plus dessus

(en voulant pointer sur un item du sous menu).

Quoi qu'il en soit, et si je peux me permettre, votre code comporte un certain

nombre d'erreurs.

Vous détectez le navigateur utilisé à l'aide des propriétés de l'objet

navigator, ce qui est une mauvaise idée. Vous devriez plutôt détecter les

méthodes et attributs que vous souhaitez utiliser.

Par exemple:

if( typeof(elem.addEventListener) != 'undefined' )
{
   // on utilise addEventListener
}
else
{
   // on utilise autre chose
}

Vous utilisez la méthode addEventListener alors que celle ci n'apporte rien dans

ce contexte. Et comme vous mettez true en troisième argument, l'évènement ne

peut se déclencher que si un évènement de même type, mais non capturant, est

déclenché en aval de l'arbre.

J'étais parti pour proposer une alternative mais la structure du menu étant loin d'être sémantiquement correcte, je n'ai pas poursuivi.

Modifié par Bobe
Lien vers le commentaire
Partager sur d’autres sites

Salut bobe,

Bonnes remarques Bobe, c'est vrai que j'ai la mauvaise habitude d'utiliser l'objet navigator pour detecter le browser, mais c'est vrai aussi que c'est beaucoup plus pratique, surtout lors du developpement d'une fonction.

Dont acte, ce n'est pas très difficile à modifier...

Je ne comprends pas bien la remarque sur addEventListener qui est référencé comme la méthode à utiliser pour initialiser un gestionnaire d'evenement sur des éléments non clickable, comme les dt du menu.

J'ai vu que vous aviez l'air beaucoup plus expérimenté que moi sur ce sujet (la propagation des évenement), peut-être quelques explications serait utiles, pour résoudre l'intéressant probleme de pierre.

Le problème apparait bien sur firefox/firebird, je vais fouiller dans cette voie, j'ai bien trouvé une solution qui fonctionne mais qui n'est pas vraiment satisfaisante.

Par contre je ne vois pas le rapport entre la sémantique du modèle de raphael et le probleme de pierre, il est vrai que je n'utiliserais pas ce format pour construire un menu mais les arguments de raphael sont recevables.

Peut-être qu'avec votre avis éclairé pourrions nous sortir un script propre ?

JP

Lien vers le commentaire
Partager sur d’autres sites

Je ne comprends pas bien la remarque sur addEventListener qui est référencé comme la méthode à utiliser...

Mais qui n'est pas supportée par certain navigateur (<- notez ici l'emploi du singulier)

Le modèle d'évènements du W3C a deux atouts majeurs:

La capture d'évènements. Elle est encore mal gérée dans les navigateurs modernes (ne parlons pas d'IE) à cause de divers bugs.

La possibilité d'enregistrer plusieurs guetteurs sur une cible.

exemple:

element.onclick = test1;

element.onclick = test2;

Seule la fonction test2() sera appellée lorsque l'évènement aura lieu.

element.addEventListener('click', test1, false);

element.addEventListener('click', test2, false);

La fonction test1() sera appellée, puis la la fonction test2() sera appellée également.

Le DOM 2 Events ne donne aucune indication sur l'ordre d'appel, mais en général, le premier guetteur enregistré est le premier appellé.

Edit: IE qui, comme tout un chacun le sait, aime lancer des petits défis à l'esprit logique, déclenche évidemment les guetteurs dans le sens inverse. Dernier enregistré, premier appellé, j'ai fait le test avec l'API propriétaire de Microsoft, attachEvent().

Les évènements capturants n'étant pas vraiment utilisable pour l'instant, à moins que l'on souhaite pouvoir enregistrer plusieurs guetteurs sur une même cible (possibilité essentielle), on utilisera plutôt à l'heure actuelle la syntaxe:

element.onevent = [nom de la fonction ou sa déclaration];

(Bon, ceci dit, je suis le premier à utiliser addEventListener, quitte à écrire des scripts de correction style "usine à gaz" pour IE ;-))

J'ai vu que vous aviez l'air beaucoup plus expérimenté que moi sur ce sujet (la propagation des évenement), peut-être quelques explications serait utiles, pour résoudre l'intéressant probleme de pierre.

Les recommandations du W3C (Technical Report http://www.w3.org/TR/) ont l'inconvénient majeur de s'adresse à deux types de personnes: Celles qui vont utiliser les "outils" décrits et celles qui vont les implémenter, d'où leur coté parfois très technique et abscon.

Je fais un exemple en vue simple:

document -> html -> body -> div1 -> div2 -> div3

Et les enregistrement d'évènements suivants:

div3.addEventListener('click', test, false);

div2.addEventListener('click', test, true);

div1.addEventListener('click', test, false);

body.addEventListener('click', test, true);

Premier cas: on clique sur le div3.

  • Le guetteur présent sur le body "capture" l'évènement 'click' et est donc déclenché



  • Le guetteur présent sur le div2 "capture" l'évènement 'click' et est donc déclenché



  • Il n'y a pas d'autres guetteurs capturants en amont du div3, fin de la phase de capture. Le guetteur sur le div3 est donc déclenché (phase "normale", sur la cible).



  • Puis on passe en phase de bouillonnement, l'évènement remonte vers la racine de l'arbre et déclenche le guetteur sur le div1.

Deuxième cas: on clique sur le div2

  • Le guetteur présent sur le body "capture" l'évènement 'click' et est donc déclenché



  • Pas d'autres guetteurs capturants en amont du div2, fin de la phase de capture. Le guetteur enregistré sur le div1 est déclenché (phase sur la cible).

Le guetteur du div2 n'a pas été déclenché car il a été enregistré comme étant capturant. Comme aucun évènement de type 'click' n'a été déclenché en aval du div2, le guetteur ne se déclenche pas.

Troisième cas: on clique sur le body

Rien ne se passe car le guetteur enregistré sur le body est en mode capturant, or, aucun évènement 'click' ne s'est produit en aval du body.

Le DOM Level 3 Events, bien que n'étant qu'à l'état de document de travail, dispose d'un schéma graphique assez représentatif du parcours effectué lors des différentes phases de l'évènement au paragraphe 1.2.1:

http://www.w3.org/TR/DOM-Level-3-Events/ev...l#Events-phases

Le site suivant donne également de bonnes informations. Regardez à la section javascript, le dernier chapitre, "events":

http://www.quirksmode.org/sitemap.html

Par contre je ne vois pas le rapport entre la sémantique du modèle de raphael et le probleme de pierre, il est vrai que je n'utiliserais pas ce format pour construire un menu mais les arguments de raphael sont recevables.

La structure n'étant pas correcte, elle devrait être rectifiée, or, le DOM dépendant entièrement de la structure du document, modifier cette structure revient le plus souvent à modifier ses scripts. Autant se baser sur une structure correcte dans ce cas :)

Peut-être qu'avec votre avis éclairé pourrions nous sortir un script propre ?

je ne suis pas là jusqu'à dimanche, mais je regarderai ensuite si je peux terminer le script que j'avais commencé hier.

Modifié par Bobe
Lien vers le commentaire
Partager sur d’autres sites

Cool,

Il est vrai que qu'il est très difficile de trouver des explications claires, concernant la propagation des evenements, particulièrement lors de la phase de bouillonement.

J'ai refait des essais sur une structure plus "classique" de listes imbriquées ou le currentTarget est l'element LI parent d'un element UL à déployer.

Le script utilise deux guetteurs (mouseover / mouse out) sur le currentTarget LI qui appelle deux fonctions qui affiche ou masque un noeud enfant, en loccurence un UL imbriqué.

Evidemment, l'event se propage gaiement jusqu'au browser qu'il finit par fermer (sur IE) :)

Je parviens bien à suivre la propagation avec les propriétés de l'event passé aux fonctions mais je ne parviens pas à stopper la propagation de l'evenement ni avec stopPropagation(), ni avec cancelBubble, et je ne comprends pas vraiment pourquoi...

J'ai tenté de contourner le problème en affectant un id, lors de l'initialisation aux éléments à traiter, en imaginant, naïevement, que l'event traitant un objet "unique" bouillonnerait tranquille dans son coin sans conséquence sur les autres objets, mais je me suis vite rendu compte de mon erreur.

J'attends avec impatience votre retour ici car la demande initiale de pierre est très inttéréssante en ce sens qu'elle permettrait de fournir une méthode élégante pour produire des menus à roll-over qui ne ressembleraient plus aux usines à gaz javascriptés, imbitables et innacessibles qui pullulent sur le web.

Cela permettrait également de fournir une alternative, peut-être plus interessante que celles que l'on peut trouver actuellement, aux limitations du hover de IE et ce en serais pas un luxe.

JP

PS: si besoin je tiens mes sources à votre disposition...

Lien vers le commentaire
Partager sur d’autres sites

Bon, désolé de ne pas m'être manifesté plus tôt :(

J'ai un peu avançé ce soir sur le script que je suis en train de confectionner.

J'essaierai de le finir demain et le publierai ici. :)

Lien vers le commentaire
Partager sur d’autres sites

Super,

En passant felicitation pour webnaute...

De mon cote j'ai enfin un script qui fonctionne sur Gecko (y compris firefox et les clones) et IE, mais comme tu à l'air plus doué que moi ;) j'attends le tiens pour confronter les méthodes :whistling:

En passant, j'ai été surpris de ne rien trouver de vraiment convaincant qui serait dans l'esprit de l'article de PPK ( séparation comportement/structure).

Et tu à raison, c'est hallucinant qu'Opéra ne supporte pas document.styleSheets... :D

JP

Lien vers le commentaire
Partager sur d’autres sites

Bon, voilà ce que ça donne pour l'instant:

http://webnaute.net/temp/Menu_dynamique

Le code javascript est fait à l'arrache et pas vraiment propre et structuré pour l'instant, mais ça fonctionne dans firefox 0.9.3 et Opera (sauf que lui me fait des bizzareries avec les flottants mais c'est sùrement corrigeable). J'adapterai pour IE demain et j'essaierai de le tester sous Konqueror également.

Super,

En passant felicitation pour webnaute...

Merci :D

En passant, j'ai été surpris de ne rien trouver de vraiment convaincant qui serait dans l'esprit de l'article de PPK ( séparation comportement/structure).

Faire du JavaScript/DOM en faisant complètement cette séparation est encore peu répandu.

Et tu à raison, c'est hallucinant qu'Opéra ne supporte pas document.styleSheets...  :D

Et toujours rien avec la version 7.60 :down:

Lien vers le commentaire
Partager sur d’autres sites

Vu,

Joli code por de l'arrache :D

Voila qui devrait satisfaire l'ami pierre duquel nous n'avons plus de nouvelles... :lol:

Deux petites questions :

hideSubMenu

referme tous les objets ul référencés, ce qui ne permet pas d'inclure des sous-sous menus ?

Et pour mon information personnelle:

Si je comprends bien, la syntaxe

hideSubMenu: function()

passe la fonction en référence de l'objet, c'est donc équivalent à une instanciation de classe ???

Pareil pour

make :

tu à un lien de référence sur cette déclaration ?

Je ne connaissais pas cette syntaxe très intéréssante, mais il est vrai que bien qu'apprentis forcené du DOM, je trouve qu'il est très compliqué de trouver des tutos/doc/références qui ne se contente pas de réécrire bêtement les specs... :nono:

En tous cas en francophonie un tuto bien fait sur le couple EcmaScript / DOM fait cruellement defaut et ne favorise pas l'effort nécessaire de séparation comportement/structure, qui n'est somme toute que l'équivalent de la séparation contenu / forme introduite par la standardisation, qui nous débarasserait des bouillies javascriptés actuelles.

JP

Lien vers le commentaire
Partager sur d’autres sites

Voila qui devrait satisfaire l'ami pierre duquel nous n'avons plus de nouvelles...  :lol:

Arf, c'est vrai. J'espère qu'on ne l'a pas effrayé avec nos considérations techniques :P

hideSubMenu

referme tous les objets ul référencés, ce qui ne permet pas d'inclure des sous-sous menus ?

Referme l'élément UL de classe 'submenu' enfant du LI sur lequel est référencé l'évènement onmouseout si l'utilisateur quitte le LI en question.

Si je comprends bien, la syntaxe
hideSubMenu: function()

passe la fonction en référence de l'objet, c'est donc équivalent à une instanciation de classe ???

Pareil pour

make :

tu à un lien de référence sur cette déclaration ?

Cela s'appelle une déclaration littérale d'un objet.

Il y a deux façons de déclarer un objet:

classique, avec un constructeur:

function monObjet(var1, var2)
{
   this.mavar1 = var1;
   this.mavar2 = var2;

   this.maMethode = function() {
   /* du code */
   };
}

// instanciation
var obj = monObjet(x, y);

Utile plutôt si plusieurs instanciations de l'objet peuvent être nécessaires.

littérale, sans constructeur:

var obj = {
   mavar1: var1,
   mavar2: var2,

   maMethode: function() {
   /* du code */
   }
};

Pas d'instanciation, l'objet est directement utilisable. Par contre, on a donc pas de constructeur. On ne peut pas créer une autre instance de l'objet.

Je ne connaissais pas cette syntaxe très intéréssante, mais il est vrai que bien qu'apprentis forcené du DOM, je trouve qu'il est très compliqué de trouver des tutos/doc/références qui ne se contente pas de réécrire bêtement les specs...  :nono:

Ah, mais cette façon de déclarer un objet est décrite dans la documentation java script:

http://devedge.netscape.com/library/manual...nt.html#1013090

En tous cas en francophonie un tuto bien fait sur le couple EcmaScript / DOM  fait cruellement defaut et ne favorise pas l'effort nécessaire de séparation comportement/structure, qui n'est somme toute que l'équivalent de la séparation contenu / forme introduite par la standardisation, qui nous débarasserait des bouillies javascriptés actuelles.

Oui, il manque vraiment une référence francophone de qualité (mais y a t-il seulement une référence anglophone de qualité ?).

Et cela représenterait un boulot conséquent d'en créer une.

P.S: Petit ajout à mon message du vendredi 27 août. Le DOM 3 Events précise que l'ordre d'appel des guetteurs enregistrés sur un objet avec addEventListener() est le même que l'ordre d'enregistrement, donc premier enregistré, premier appellé :)

If two event listeners {A1, A2}, which are part of the same group, are registered one after the other (A1, then A2) for the same phase, the DOM event flow guarantees their triggering order (A1, then A2).

http://www.w3.org/TR/DOM-Level-3-Events/ev...vents-listeners (paragraphe 1.2.2.2 - "event groups")

Modifié par Bobe
Lien vers le commentaire
Partager sur d’autres sites

Voila qui devrait satisfaire l'ami pierre duquel nous n'avons plus de nouvelles...

Je ne me manifeste pas mais je suis toujours là. Je suis la conversation mais je dois avouer que je suis un peu paumé.

En tout cas, je vois que le sujet vous passione. Je suis content d'avoir initié la conversation. Maintennant, j'attend la solution pratique que je pourrai appliquer sur mon site.

Merci à vous tous.

Lien vers le commentaire
Partager sur d’autres sites

http://dev.webnaute.net/BAS/Menu_dynamique

Bon, alors là:

C'est parfait sous Firefox et autres geckos

Sous IE, il me met le dernier item, "menu 5" à la ligne et a tendance à ne pas refermer les sous menus parfois. <_<

Sous Opera 7.60p1, presque parfait, sauf que le premier item, "accueil", est caché :blink:

Sous Opera 7.50, c'est comme si je n'avais pas mis float: left;

Bon, que des problèmes de CSS en tout cas, le script en lui même semble fonctionner parfaitement :)

Petits extra:

La CSS est agencée de telle manière que si l'écran est trop petit, les items se mettent sur plusieurs lignes et dans un alignement parfait. J'aime beaucoup :)

Sur un click sur un lien d'un sous menu, le sous menu est refermé au lieu de rester ouvert et l'attention (focus) est retiré du lien.

À tester éventuellement sur d'autres navigateurs (au moins Safari). J'essaierai de corriger les problèmes de CSS.

Modifié par Bobe
Lien vers le commentaire
Partager sur d’autres sites

http://dev.webnaute.net/BAS/Menu_dynamique

Sous IE, il me met le dernier item, "menu 5" à la ligne et a tendance à ne pas refermer les sous menus parfois.  <_<

Bon, pour le menu5 qui se met à la ligne, c'est parce que IE limite la largeur d'un bloc positionné à celle de son bloc conteneur. Et comme j'avais mis des marges latérales à l'élément BODY, et que le UL racine du menu héritait de la largeur calculée de BODY, il n'y avait pas assez de place pour que tous les LI soient alignés.

Sous Opera 7.50, c'est comme si je n'avais pas mis float: left;

C'est un bug:

http://www.latchman.org/sam/index.php/2004...peraIlFlottePas

Corrigé dans la 7.60.

Sous Opera 7.60p1, presque parfait, sauf que le premier item, "accueil", est caché  :blink:

Là, c'est encore pire avec les corrections que j'ai faite...

Mais si je désactive le javascript et que je recharge le document, je peux constater que l'affichage est parfait avec les menus dépliés, donc ce n'est pas un problème de CSS. Et je suis sùr de la partie du script qui masque les sous menus au chargement du document, donc une mauvaise gestion dynamique des styles de la part de Opera 7.60 à priori. (Bon, en même temps, c'est une preview1, faudra voir avec une version plus stable de cette branche).

Tip: Toujours donner des marges externes et internes nulle à l'élément body. Sinon, cela peut poser problème avec IE. Là, le UL positionné se retrouvait positionné au dessus de l'élément HTML (car body avait un margin-top de 4%) et lorsqu'on arrivait avec le curseur de la souris sur l'espace séparant l'item de menu du bloc de sous menu, IE considérait qu'on passait sur l'élément html et refermait donc le sous menu. Cela ne fait pas ce bug absurde si le UL du menu est au dessus du BODY (margin: 0; padding: 0; sur l'élément BODY donc pour ne pas avoir de surprise) ou autre élément.

Modifié par Bobe
Lien vers le commentaire
Partager sur d’autres sites

Resultat des tests :

Je confirme que c'est bon pour IE6, 5.5, Moz, FF, OP 7.54 (j'ai pas trouvé la 7.6...)

Seul petit probleme sur Netscape 7 où il est difficile d'accrocher le sous-menu, mais bon, netscape 7 est particulièrement buggé sur ce genre de comportement.

Pour opera, de toutes manières, ils modifient tellement l'implémentation CSS/DOM de version en version qu'il est bien difficile d'y trouver son compte, sans compter qu'ils tentent d'assurer une compatibilité Gecko/IE en mélangeant les méthodes ce qui rends encore plus obscur le developpement sur cette plateforme en provoquant des effets pour le moins étrange.

Le dernier en date en ce qui me concerne: on peut obtenir un focus sur un lien en display:none ou visibility:hidden avec la navigation tabulaire, qui en plus utilise une séquence de touche propriétaire, le pied total quoi !... :lol:

Le script de bobe parait donc être une bonne base pour gérer facilement des menus en rollover scripté.

C'est pierre qui va être content :) :)

Seul petit bémol, à mon gout et parce que j'aime bien triturer le code, je trouve la syntaxe un chouia compliqué à suivre, donc à adapter eventuellement.

Mais bon ce doit être juste une question d'habitude... :D

Lien vers le commentaire
Partager sur d’autres sites

Seul petit probleme sur Netscape 7 où il est difficile d'accrocher le sous-menu, mais bon, netscape 7 est particulièrement buggé sur ce genre de comportement.

Quelle version de Netscape 7 ? Ou mieux, la version de Mozilla sur laquelle est basé le netscape 7 avec lequel tu as fait le test.

Le dernier en date en ce qui me concerne: on peut obtenir un focus sur un lien en display:none ou visibility:hidden avec la navigation tabulaire, qui en plus utilise une séquence de touche propriétaire, le pied total quoi !...  :lol:

Ah oui, interessant ce bug :)

Seul petit bémol, à mon gout et parce que j'aime bien triturer le code, je trouve la syntaxe  un chouia compliqué à suivre, donc à adapter eventuellement.

Je peux fournir des explications si besoin.

Enfin concernant la partie script, je pense que c'est assez propre et clair (au niveau de la lecture).

J'ai essayé de faire quelque chose d'un peu modulaire. On peut par exemple déplier manuellement un sous menu, il suffit de faire:

ComplexMenu.unfoldingSubMenu(obj);// obj est l'objet représentant un UL de classe 'submenu'

Pratique pour que, par exemple, le sous menu correspondant à la section dans laquelle on se trouve soit déplié par défaut (Ce serait bof dans l'affichage par défaut qu'on a là, mais ça pourrait être bien avec un affichage en deux colonnes avec le menu d'un coté et le contenu de l'autre):

if( typeof(document.getElementById) != 'undefined' )
{
   window.onload = function() {
       ComplexMenu.initialize();
       ComplexMenu.unfoldingSubMenu(document.getElementById('section-active'));
   }
}

Avec un id 'section-active' sur le UL de classe 'submenu' correspondant à la section ou rubrique du site dans laquelle on se trouve.

Bon, ça pourrait évidemment être encore plus personnalisable. Par exemple, désactiver le dépliage d'un sous menu.

J'en profite pour corriger un petit bug, hideOnActicate -> hideOnActivate :rolleyes:

Modifié par Bobe
Lien vers le commentaire
Partager sur d’autres sites

Merci beaucoup, j'ai appliqué cette méthode sur mon site et j'en suis très satisfait. C'est super.

Ca a tout de même une bien meilleure gueule qu'avec les listes de définitions et les onMousOver.

Il manque une ressource francophone sur le sujet, vous ne trouvez pas ?

Modifié par pierredureau
Lien vers le commentaire
Partager sur d’autres sites

Merci beaucoup, j'ai appliqué cette méthode sur mon site et j'en suis très satisfait. C'est super.

Ah, bonne nouvelle :)

Il manque une ressource francophone sur le sujet, vous ne trouvez pas ?

En effet

Lien vers le commentaire
Partager sur d’autres sites

C'est sur qu'une ressource francophone sur la séparation comportement/structure ça ferais sacrément du bien... :)

Mais, en même temps cela demanderais pas mal de compétences techniques, en plus d'un redactionnel impecable, car le DOM est comme tu l'à vu un truc très technique, un peut déroutant au départ et qui, de fait, se borne souvent à getElementById et getElementsByTagName...

Mais bon si ya une iniative sur ce sujet je veux bien en être, je suis venu au DOM et au concept de séparation comportement/structure par la problématique de l'accessibilité, (qui est l'essentiel de mon activité pro), autrement dit pour EcmaScript, comment rendre un script non intrusif ni destructeur pour l'accessiblité d'un contenu.

Je peux apporter pas mal d'éclairage la dessus, par contre je ne me risquerais pas, en tout cas pas tout de suite sur de la rédaction de ressource plus technique, les scripts que je produis fonctionnent bien mais face à une compétence comme celle de l'ami bobe, je mesure le chemin qu'il me reste à parcourir, (attention.. Top départ !) ;).

En passant, merci pour la leçon bobe, et si tu à une minute pour glisser les deux ou trois commentaires qui vont bien, je suis sur que tu vas créer des vocations ... :) :)

Sinon j'ai trouvé récemment un bon lien francophone pour aborder l'implémentation du DOM (malgrés son titre ;) ) ici :

cours DHTML

JP

Lien vers le commentaire
Partager sur d’autres sites

Mais, en même temps cela demanderais pas mal de compétences techniques, en plus d'un redactionnel impecable, car le DOM est comme tu l'à vu un truc très technique,  un peut déroutant au départ et qui, de fait, se borne souvent à getElementById et getElementsByTagName...

Mais bon si ya une iniative sur ce sujet je veux bien en être, je suis venu au DOM et au concept de séparation comportement/structure par la problématique de l'accessibilité, (qui est l'essentiel de mon activité pro), autrement dit pour EcmaScript, comment rendre un script non intrusif ni destructeur pour l'accessiblité d'un contenu.

Je peux apporter pas mal d'éclairage la dessus, par contre je ne me risquerais pas, en tout cas pas tout de suite sur de la rédaction de ressource plus technique, les scripts que je produis fonctionnent bien mais face à une compétence comme celle de l'ami bobe, je mesure le chemin qu'il me reste à parcourir, (attention.. Top départ !) ;).

Je ne m'y risquerais pas non plus (complètement autodidacte, j'ai des difficultés à transmettre mes connaissances et une pratique rédactionnelle médiocre).

En passant, merci pour la leçon bobe, et si tu à une minute pour glisser les deux ou trois commentaires qui vont bien, je suis sur que tu vas créer des vocations ... :) :)

Comment ça ? :unsure:

Lien vers le commentaire
Partager sur d’autres sites

Veuillez vous connecter pour commenter

Vous pourrez laisser un commentaire après vous êtes connecté.



Connectez-vous maintenant
×
×
  • Créer...