Résoudre les problèmes d'héritage des événements

Parent Previous Next



Résoudre les problèmes d'héritage des événements


En Javascript, il existe un problème fréquent que nous vous proposons d'étudier et de résoudre afin de vous éviter bien des peines lorsque cela vous arrivera.


Le problème


Plutôt que de vous expliquer le problème, nous allons vous le faire constater. Prenez donc ce code HTML ainsi que ce code CSS :

<div id="myDiv">

  <div>Texte 1</div>

  <div>Texte 2</div>

  <div>Texte 3</div>

  <div>Texte 4</div>

</div>

 

<div id="results"></div>

#myDiv, #results {

  margin: 50px;

}

 

#myDiv {

  padding: 10px;

  width: 200px;

  text-align: center;

  background-color: #000;

}

 

#myDiv div {

  margin: 10px;

  background-color: #555;

}


Maintenant, voyons ce que nous souhaitons obtenir. Notre but ici est de faire en sorte de détecter quand le curseur entre sur notre élément #myDiv et quand il en ressort. Vous allez donc penser qu'il n'y a rien de plus facile et vous lancer dans un code de ce genre :

var myDiv = document.getElementById('myDiv'),

    results = document.getElementById('results');

 

myDiv.onmouseover = function() {

    results.innerHTML += "Le curseur vient d'entrer.";

};

 

myDiv.onmouseout = function() {

    results.innerHTML += "Le curseur vient de sortir.";

};


Eh bien soit !


Alors ? Avez-vous essayé de faire passer votre curseur sur toute la surface du <div>#myDiv ? Il y a effectivement quelques lignes en trop qui s'affichent dans nos résultats…


Je ne vois absolument pas d'où ça peut venir…


Si cela peut vous rassurer, personne ne voit bien d'où cela peut venir au premier coup d'œil. En fait, le souci est tout bête et a déjà été fortement évoqué au travers de ce chapitre, relisez donc ceci :

Citation

Certains événements appliqués à un élément parent peuvent se propager d'eux-mêmes aux éléments enfants, c'est le cas des événements mouseover,  mouseout, mousemove,  click… ainsi que d'autres événements moins utilisés.

Voici notre problème : les enfants héritent des propriétés des événements susnommés appliqués aux éléments parents. Ainsi, lorsque vous déplacez votre curseur depuis le <div>#myDiv jusqu'à un <div> enfant, vous allez déclencher l'événement mouseout sur  #myDiv et l'événement mouseover sur le <div> enfant.


La solution


Afin de pallier ce problème, il existe une solution assez tordue. Vous souvenez-vous de la propriété relatedTarget abordée dans ce chapitre ? Son but va être de détecter quel est l'élément vers lequel le curseur se dirige ou de quel élément il provient.


Ainsi, nous avons deux cas de figure :


Mettons cela en pratique avec l'événement mouseover pour commencer. Voici le code d'origine :

myDiv.onmouseover = function() {

    results.innerHTML += "Le curseur vient d'entrer.";

};


Il nous faut maintenant obtenir l'élément de provenance. Puisque relatedTarget n'est pas supporté par les vieilles versions d'Internet Explorer, nous allons devoir ruser un peu :

myDiv.onmouseover = function(e) {

 

    e = e || window.event; // Compatibilité IE

    var relatedTarget = e.relatedTarget || e.fromElement; // Idem

 

    results.innerHTML += "Le curseur vient d'entrer.";

 

};


Maintenant, il nous faut savoir si l'élément en question est un enfant direct de myDiv ou non. La solution consiste à remonter tout le long de ses éléments parents jusqu'à tomber soit sur myDiv, soit sur l'élément<body> qui désigne l'élément HTML le plus haut dans notre document. Il va donc nous falloir une boucle while :

myDiv.onmouseover = function(e) {

 

    e = e || window.event; // Compatibilité IE

    var relatedTarget = e.relatedTarget || e.fromElement; // Idem

 

    while (relatedTarget != myDiv && relatedTarget.nodeName != 'BODY') {

        relatedTarget = relatedTarget.parentNode;

    }

 

    results.innerHTML += "Le curseur vient d'entrer.";

 

};


Ainsi, nous retrouverons dans notre variable relatedTarget le premier élément trouvé qui correspond à nos critères, donc soit myDiv, soit <body>. Il nous suffit alors d'insérer une condition qui exécutera le code de notre événement uniquement dans le cas où la variable relatedTarget ne pointe pas sur l'élément myDiv :

myDiv.onmouseover = function(e) {

 

    e = e || window.event; // Compatibilité IE

    var relatedTarget = e.relatedTarget || e.fromElement; // Idem

 

    while (relatedTarget != myDiv && relatedTarget.nodeName != 'BODY') {

        relatedTarget = relatedTarget.parentNode;

    }

 

    if (relatedTarget != myDiv) {

        results.innerHTML += "Le curseur vient d'entrer.";

    }

 

};


Cependant, il reste encore un petit cas de figure qui n'a pas été géré et qui peut être source de problèmes ! Comme vous le savez, la balise <body> ne couvre pas forcément la page Web complète de votre navigateur, ce qui fait que votre curseur peut provenir d'un élément situé encore plus haut que la balise<body>. Cet élément correspond à la balise <html> — soit l'élément document en Javascript —, il nous faut donc faire une petite modification afin de bien préciser que si le curseur provient de document il ne peut forcément pas provenir de myDiv :

myDiv.onmouseover = function(e) {

 

    e = e || window.event; // Compatibilité IE

    var relatedTarget = e.relatedTarget || e.fromElement; // Idem

 

    while (relatedTarget != myDiv && relatedTarget.nodeName != 'BODY' && relatedTarget != document) {

        relatedTarget = relatedTarget.parentNode;

    }

 

    if (relatedTarget != myDiv) {

        results.innerHTML += "Le curseur vient d'entrer.";

    }

 

};


Voilà ! Maintenant, notre événement mouseover fonctionne comme nous le souhaitions ! Rassurez-vous, vous avez fait le plus gros, il ne nous reste plus qu'à adapter un peu le code pour l'événement mouseout. Cet événement va utiliser le même code que celui de mouseover à deux choses près :


Ce qui nous donne donc ceci :

myDiv.onmouseout = function(e) {

 

    e = e || window.event; // Compatibilité IE

    var relatedTarget = e.relatedTarget || e.toElement; // Idem

 

    while (relatedTarget != myDiv && relatedTarget.nodeName != 'BODY' && relatedTarget != document) {

        relatedTarget = relatedTarget.parentNode;

    }

 

    if (relatedTarget != myDiv) {

        results.innerHTML += "Le curseur vient de sortir.<br />";

    }

 

};


Enfin, nous avons terminé ! 

L'étude de ce problème était quelque peu avancée par rapport à vos connaissances actuelles, sachez que vous n'êtes pas obligés de retenir la solution. Retenez cependant qu'elle existe et que vous pouvez la trouver ici, dans ce chapitre, car ce genre de soucis peut être très embêtant dans certains cas, notamment quand il s'agit de faire des animations.


En résumé



Créé avec HelpNDoc Personal Edition: Générateur d'aide complet

Site à deux balles