Plongeont dans plus de détails sur les événements qui se produisent lorsque la souris se déplace entre les éléments.
Events mouseover/mouseout, relatedTarget
L’évènement mouseover
est exécuté lorsqu’un pointeur de la souris survole un élément, et mouseout
– lorsqu’il le quitte.
Ces événements sont spéciaux, car ils ont la propriété relatedTarget
. Cette propriété complète target
. Quand une souris quitte un élément pour un autre, l’un d’eux devient target
, et l’autre – relatedTarget
.
Pour mouseover
:
event.target
– l’élément que survole la souris.event.relatedTarget
– voici l’élément d’origine la souris (relatedTarget
→target
).
A l’inverse du mouseout
:
event.target
– est l’élément que la souris a quitté.event.relatedTarget
– est le nouvel élément situé sous le pointeur, celui pour lequel la souris a quitté (target
→relatedTarget
).
Dans l’exemple ci-dessous chaque aspect facial est un élément. Lorsque vous déplacez la souris, vous pouvez voir les évènements de souris dans la zone de texte.
Chaque événement contient les informations sur target
et relatedTarget
:
container.onmouseover = container.onmouseout = handler;
function handler(event) {
function str(el) {
if (!el) return "null"
return el.className || el.tagName;
}
log.value += event.type + ': ' +
'target=' + str(event.target) +
', relatedTarget=' + str(event.relatedTarget) + "\n";
log.scrollTop = log.scrollHeight;
if (event.type == 'mouseover') {
event.target.style.background = 'pink'
}
if (event.type == 'mouseout') {
event.target.style.background = ''
}
}
body,
html {
margin: 0;
padding: 0;
}
#container {
border: 1px solid brown;
padding: 10px;
width: 330px;
margin-bottom: 5px;
box-sizing: border-box;
}
#log {
height: 120px;
width: 350px;
display: block;
box-sizing: border-box;
}
[class^="smiley-"] {
display: inline-block;
width: 70px;
height: 70px;
border-radius: 50%;
margin-right: 20px;
}
.smiley-green {
background: #a9db7a;
border: 5px solid #92c563;
position: relative;
}
.smiley-green .left-eye {
width: 18%;
height: 18%;
background: #84b458;
position: relative;
top: 29%;
left: 22%;
border-radius: 50%;
float: left;
}
.smiley-green .right-eye {
width: 18%;
height: 18%;
border-radius: 50%;
position: relative;
background: #84b458;
top: 29%;
right: 22%;
float: right;
}
.smiley-green .smile {
position: absolute;
top: 67%;
left: 16.5%;
width: 70%;
height: 20%;
overflow: hidden;
}
.smiley-green .smile:after,
.smiley-green .smile:before {
content: "";
position: absolute;
top: -50%;
left: 0%;
border-radius: 50%;
background: #84b458;
height: 100%;
width: 97%;
}
.smiley-green .smile:after {
background: #84b458;
height: 80%;
top: -40%;
left: 0%;
}
.smiley-yellow {
background: #eed16a;
border: 5px solid #dbae51;
position: relative;
}
.smiley-yellow .left-eye {
width: 18%;
height: 18%;
background: #dba652;
position: relative;
top: 29%;
left: 22%;
border-radius: 50%;
float: left;
}
.smiley-yellow .right-eye {
width: 18%;
height: 18%;
border-radius: 50%;
position: relative;
background: #dba652;
top: 29%;
right: 22%;
float: right;
}
.smiley-yellow .smile {
position: absolute;
top: 67%;
left: 19%;
width: 65%;
height: 14%;
background: #dba652;
overflow: hidden;
border-radius: 8px;
}
.smiley-red {
background: #ee9295;
border: 5px solid #e27378;
position: relative;
}
.smiley-red .left-eye {
width: 18%;
height: 18%;
background: #d96065;
position: relative;
top: 29%;
left: 22%;
border-radius: 50%;
float: left;
}
.smiley-red .right-eye {
width: 18%;
height: 18%;
border-radius: 50%;
position: relative;
background: #d96065;
top: 29%;
right: 22%;
float: right;
}
.smiley-red .smile {
position: absolute;
top: 57%;
left: 16.5%;
width: 70%;
height: 20%;
overflow: hidden;
}
.smiley-red .smile:after,
.smiley-red .smile:before {
content: "";
position: absolute;
top: 50%;
left: 0%;
border-radius: 50%;
background: #d96065;
height: 100%;
width: 97%;
}
.smiley-red .smile:after {
background: #d96065;
height: 80%;
top: 60%;
left: 0%;
}
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="container">
<div class="smiley-green">
<div class="left-eye"></div>
<div class="right-eye"></div>
<div class="smile"></div>
</div>
<div class="smiley-yellow">
<div class="left-eye"></div>
<div class="right-eye"></div>
<div class="smile"></div>
</div>
<div class="smiley-red">
<div class="left-eye"></div>
<div class="right-eye"></div>
<div class="smile"></div>
</div>
</div>
<textarea id="log"> Les évènements apparaîtront ici!
</textarea>
<script src="script.js"></script>
</body>
</html>
relatedTarget
peut être null
La propriété relatedTarget
peut être null
.
C’est normal et cela signifie simplement que la souris de provient pas d’un autre élément, mais hors de la fenêtre Windows. Ou bien qu’elle a quitté la fenêtre Windows.
Nous devons garder cette éventualité à l’esprit lorsqu’on utilise event.relatedTarget
dans notre code. Si nous accédons a la propriété event.relatedTarget.tagName
, alors il y aura une erreur.
Ignorer des éléments
L’évènement mousemove
se déclenche lorsque la souris se déplace. Mais cela ne signifie pas chaque pixel mène a un évènement.
Le navigateur vérifie la position de la souris de temps en temps. Et s’il remarque des changements, déclenche les événements.
Cela signifie que si le visiteur déplace la souris très rapidement, certains éléments DOM peuvent être ignorés :
Si la souris se déplace très rapidement de #FROM
aux éléments #TO
telle que décrite en haut, alors le <div>
intermédiaire (ou certains d’entre eux) peuvent être sautés. L’évènement mouseout
peut être déclenche sur #FROM
et ensuite immédiatement le mouseover
sur #TO
.
C’est bon pour la performance, car s’il peut y avoir beaucoup d’éléments intermédiaires. Nous ne voulons pas vraiment traiter dans et hors de chacun d’entre eux.
D’autre part, nous devons garder à l’esprit que le pointeur de la souris ne “visite” pas tous les éléments le long du chemin. Il peut “sauter”.
En particulier, il est possible que le pointeur saute directement au centre de la page depuis l’extérieur de la fenêtre. Dans ce cas relatedTarget
est null
, parce qu’il venait de “nulle part” :
Vous pouvez le vérifier “en direct” sur un banc d’essai ci-dessous.
Son code HTML comporte deux éléments imbriqués: la <div id="child">
est à l’intérieur de <div id="parent">
. Si vous déplacez rapidement la souris dessus, alors peut-être que seule la div enfant déclenchera les événements, ou peut-être la div parent, ou peut-être qu’il n’y aura aucun événement.
Déplacez également le pointeur dans la div
enfant, puis le déplacer rapidement en dehors à travers le parent. Si le mouvement est assez rapide, l’élément parent est ignoré. La souris traversera l’élément parent sans le remarquer.
let parent = document.getElementById('parent');
parent.onmouseover = parent.onmouseout = parent.onmousemove = handler;
function handler(event) {
let type = event.type;
while (type.length < 11) type += ' ';
log(type + " target=" + event.target.id)
return false;
}
function clearText() {
text.value = "";
lastMessage = "";
}
let lastMessageTime = 0;
let lastMessage = "";
let repeatCounter = 1;
function log(message) {
if (lastMessageTime == 0) lastMessageTime = new Date();
let time = new Date();
if (time - lastMessageTime > 500) {
message = '------------------------------\n' + message;
}
if (message === lastMessage) {
repeatCounter++;
if (repeatCounter == 2) {
text.value = text.value.trim() + ' x 2\n';
} else {
text.value = text.value.slice(0, text.value.lastIndexOf('x') + 1) + repeatCounter + "\n";
}
} else {
repeatCounter = 1;
text.value += message + "\n";
}
text.scrollTop = text.scrollHeight;
lastMessageTime = time;
lastMessage = message;
}
#parent {
background: #99C0C3;
width: 160px;
height: 120px;
position: relative;
}
#child {
background: #FFDE99;
width: 50%;
height: 50%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
textarea {
height: 140px;
width: 300px;
display: block;
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="parent">parent
<div id="child">child</div>
</div>
<textarea id="text"></textarea>
<input onclick="clearText()" value="Clear" type="button">
<script src="script.js"></script>
</body>
</html>
mouseover
est déclenché, il doit y avoir mouseout
En cas de mouvements rapides de la souris, les éléments intermédiaires peuvent être ignorés, mais une chose est sûre : si le pointeur est “officiellement” entré dans un élément (événement mouseover
généré), alors en le quittant, on aura toujours mouseout
.
Mouseout en quittant pour un enfant
Une caractéristique importante de mouseout
– il se déclenche lorsque le pointeur se déplace d’un élément à son descendant, par ex. de #parent
à #enfant
dans ce code HTML :
<div id="parent">
<div id="child">...</div>
</div>
Si nous sommes sur #parent
, puis déplaçons le pointeur plus profondément dans #enfant
, nous obtenons mouseout
sur #parent
!
Cela semble étrange, mais peut être facilement expliqué.
Selon la logique du navigateur, le curseur de la souris ne peut survoler qu’un seul élément à tout moment – l’élément le plus imbriqué et le plus élevé par z-index.
Donc, s’il passe à un autre élément (même un descendant), alors il quitte le précédent.
Veuillez noter un autre détail important du traitement de l’événement.
L’événement mouseover
sur un descendant “bubble up” (remonte). Donc, si #parent
a un gestionnaire mouseover
, il se déclenche :
Vous pouvez le voir très bien dans l’exemple ci-dessous : <div id="child ">
est à l’intérieur de <div id="parent">
. Il existe des gestionnaires mouseover/out
sur l’élément #parent
qui fournissent les détails de l’événement.
Si vous déplacez la souris de #parent
à #enfant
, vous voyez deux événements sur #parent
:
mouseout [target: parent]
(quitte le parent), alorsmouseover [target: child]
(est arrivé à l’enfant, bubbled).
function mouselog(event) {
let d = new Date();
text.value += `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} | ${event.type} [target: ${event.target.id}]\n`.replace(/(:|^)(\d\D)/, '$10$2');
text.scrollTop = text.scrollHeight;
}
#parent {
background: #99C0C3;
width: 160px;
height: 120px;
position: relative;
}
#child {
background: #FFDE99;
width: 50%;
height: 50%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
textarea {
height: 140px;
width: 300px;
display: block;
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="parent" onmouseover="mouselog(event)" onmouseout="mouselog(event)">parent
<div id="child">child</div>
</div>
<textarea id="text"></textarea>
<input type="button" onclick="text.value=''" value="Clear">
<script src="script.js"></script>
</body>
</html>
Comme indiqué, lorsque le pointeur passe de l’élément #parent
à l’élément #child
, deux gestionnaires se déclenchent sur l’élément parent : mouseout
et mouseover
:
parent.onmouseout = function(event) {
/* event.target: élément parent */
};
parent.onmouseover = function(event) {
/* event.target: élément enfant */
};
Si nous n’examinons pas event.target
à l’intérieur des gestionnaires, il peut alors sembler que le pointeur de la souris a quitté l’élément #parent
, puis est immédiatement revenu dessus.
Mais ce n’est pas le cas! Le pointeur se trouve toujours sur le parent, il s’est déplacé plus profondément dans l’élément enfant.
S’il y a des actions lorsque vous quittez l’élément parent, par exemple une animation qui s’exécute dans parent.onmouseout
, nous ne le souhaitons généralement pas lorsque le pointeur va plus en profondeur dans #parent
.
Pour l’éviter, nous pouvons vérifier relatedTarget
dans le gestionnaire et, si la souris est toujours dans l’élément, alors ignorer cet événement.
Alternativement, nous pouvons utiliser d’autres événements : mouseenter
et mouseleave
, que nous allons couvrir maintenant, car ils n’ont pas ce genre de problèmes.
Evènements mouseenter and mouseleave
Les évènements mouseenter/mouseleave
sont comme mouseover/mouseout
. Ils se déclenchent lorsque le pointeur de la souris entre/sort de l’élément.
Mais il y a deux différences importantes :
- Les transitions à l’intérieur de l’élément, vers/depuis les descendants, ne sont pas comptées.
- Les évènements
mouseenter/mouseleave
ne “bubble” pas.
Ces événements sont extrêmement simples.
Quand le pointeur entre dans un élément – mouseenter
se déclenche. L’emplacement exact du pointeur à l’intérieur de l’élément ou de ses descendants n’a pas d’importance.
Quand le pointeur quitte un élément – mouseleave
se déclenche.
Cet exemple est similaire à celui ci-dessus, mais maintenant l’élément supérieur a mouseenter/mouseleave
au lieu de mouseover/mouseout
.
Comme vous pouvez le voir, les seuls événements générés sont ceux liés au déplacement du pointeur dans et hors de l’élément supérieur. Rien ne se passe lorsque le pointeur se dirige vers l’enfant et vice-versa. Les transitions entre les descendants sont ignorées.
function mouselog(event) {
let d = new Date();
text.value += `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} | ${event.type} [target: ${event.target.id}]\n`.replace(/(:|^)(\d\D)/, '$10$2');
text.scrollTop = text.scrollHeight;
}
#parent {
background: #99C0C3;
width: 160px;
height: 120px;
position: relative;
}
#child {
background: #FFDE99;
width: 50%;
height: 50%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
textarea {
height: 140px;
width: 300px;
display: block;
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="parent" onmouseenter="mouselog(event)" onmouseleave="mouselog(event)">parent
<div id="child">child</div>
</div>
<textarea id="text"></textarea>
<input type="button" onclick="text.value=''" value="Clear">
<script src="script.js"></script>
</body>
</html>
Délégation des évènements
Les évènements mouseenter/leave
sont très simple et facile à utiliser. Mais il ne remonte pas. Donc nous ne pouvons pas utiliser la délégation d’évènements sur eux.
Imaginez qu’on veuille gérer les évènements de souris enter/leave pour les cellules d’un tableau et qu’il ait une centaine de cellules.
La solution naturelle serait de définir le gestionnaire sur <table>
et d’y traiter les événements. Mais mouseenter/leave
ne “bubble” pas. Donc, si un tel événement se produit sur <td>
, alors seul un gestionnaire sur ce <td>
est capable de l’attraper.
Les gestionnaires pour mouseenter/leave
sur <table>
ne se déclenche que lorsque le pointeur entre/sort du tableau dans son ensemble. Il est impossible d’obtenir des informations sur les transitions à l’intérieur.
Alors, utilisons mouseover/mouseout
.
Commençons par des gestionnaires simples qui mettent en évidence l’élément sous la souris :
// mettons en évidence un élément sous le pointeur
table.onmouseover = function(event) {
let target = event.target;
target.style.background = 'pink';
};
table.onmouseout = function(event) {
let target = event.target;
target.style.background = '';
};
Les voici en action. Lorsque la souris se déplace sur les éléments de ce tableau, celui qui est survolé est mis en évidence:
table.onmouseover = function(event) {
let target = event.target;
target.style.background = 'pink';
text.value += `over -> ${target.tagName}\n`;
text.scrollTop = text.scrollHeight;
};
table.onmouseout = function(event) {
let target = event.target;
target.style.background = '';
text.value += `out <- ${target.tagName}\n`;
text.scrollTop = text.scrollHeight;
};
#text {
display: block;
height: 100px;
width: 456px;
}
#table th {
text-align: center;
font-weight: bold;
}
#table td {
width: 150px;
white-space: nowrap;
text-align: center;
vertical-align: bottom;
padding-top: 5px;
padding-bottom: 12px;
cursor: pointer;
}
#table .nw {
background: #999;
}
#table .n {
background: #03f;
color: #fff;
}
#table .ne {
background: #ff6;
}
#table .w {
background: #ff0;
}
#table .c {
background: #60c;
color: #fff;
}
#table .e {
background: #09f;
color: #fff;
}
#table .sw {
background: #963;
color: #fff;
}
#table .s {
background: #f60;
color: #fff;
}
#table .se {
background: #0c3;
color: #fff;
}
#table .highlight {
background: red;
}
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<table id="table">
<tr>
<th colspan="3"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>
</tr>
<tr>
<td class="nw"><strong>Northwest</strong>
<br>Metal
<br>Silver
<br>Elders
</td>
<td class="n"><strong>North</strong>
<br>Water
<br>Blue
<br>Change
</td>
<td class="ne"><strong>Northeast</strong>
<br>Earth
<br>Yellow
<br>Direction
</td>
</tr>
<tr>
<td class="w"><strong>West</strong>
<br>Metal
<br>Gold
<br>Youth
</td>
<td class="c"><strong>Center</strong>
<br>All
<br>Purple
<br>Harmony
</td>
<td class="e"><strong>East</strong>
<br>Wood
<br>Blue
<br>Future
</td>
</tr>
<tr>
<td class="sw"><strong>Southwest</strong>
<br>Earth
<br>Brown
<br>Tranquility
</td>
<td class="s"><strong>South</strong>
<br>Fire
<br>Orange
<br>Fame
</td>
<td class="se"><strong>Southeast</strong>
<br>Wood
<br>Green
<br>Romance
</td>
</tr>
</table>
<textarea id="text"></textarea>
<input type="button" onclick="text.value=''" value="Clear">
<script src="script.js"></script>
</body>
</html>
Ces gestionnaires fonctionnent lorsqu’on se déplace sur n’importe quel élément dans le tableau.
Dans notre cas, nous aimerions gérer les transitions entre les cellules du tableau <td>
: entrer dans une cellule et la quitter. Les autres transitions, comme à l’intérieur de la cellule ou à l’extérieur de celles-ci, ne nous intéressent pas. Filtrons-les.
Voici ce que nous pouvons faire :
- Mémoriser le
actuellement sélectionné dans une variable, appelons le currentElem
.- Sur
mouseover
– ignorer l’événement si nous sommes toujours dans le<td>
actuel.- Sur
mouseout
– ignorer si nous n’avons pas quitté le<td>
actuel.Voici un exemple de code qui prend en compte toutes les situations possibles :
// <td> sous la souris actuellement(le cas échéant) let currentElem = null; table.onmouseover = function(event) { // avant d'entrer dans un nouvel élément, la souris quitte toujours le précédent // Si currentElem est défini, nous n'avons pas quitté le précédent <td>, // c'est un mouseover à l'intérieur, ignore l'événement if (currentElem) return; let target = event.target.closest("td"); // nous ne sommes pas passés dans un <td> - ignorer if (!target) return; // déplacé dans <td>, mais en dehors de notre tableau (possible en cas de tableaux imbriquées) // ignorer if (!table.contains(target)) return; // hourra! nous sommes entrés dans un nouveau <td> currentElem = target; onEnter(currentElem); }; table.onmouseout = function(event) { // si nous sommes en dehors de tout <td> maintenant, alors ignorez l'événement // c'est probablement un mouvement à l'intérieur du tableau, mais en dehors des <td>, // par exemple. d'un <tr> à un autre <tr> if (!currentElem) return; // nous quittons l'élément - où aller ? Peut-être à un descendant ? let relatedTarget = event.relatedTarget; while (relatedTarget) { // monte dans la chaîne parente et vérifie - si nous sommes toujours dans currentElem // alors c'est une transition interne - l'ignorer if (relatedTarget == currentElem) return; relatedTarget = relatedTarget.parentNode; } // nous avons quitté le <td>. vraiment. onLeave(currentElem); currentElem = null; }; // any functions to handle entering/leaving an element function onEnter(elem) { elem.style.background = "pink"; // show that in textarea text.value += `over -> ${currentElem.tagName}.${currentElem.className}\n`; text.scrollTop = 1e6; } function onLeave(elem) { elem.style.background = ""; // show that in textarea text.value += `out <- ${elem.tagName}.${elem.className}\n`; text.scrollTop = 1e6; }
Les caractéristiques importantes sont les suivantes:
- On utilise la délégation d’événements pour gérer l’entrée/sortie de tout
<td>
à l’intérieur de la table. On s’appuie donc sur lemouseover/out
au lieu dumouseenter/leave
qui ne fait pas de bulles et ne permet donc aucune délégation… - Les événements supplémentaires, tels que le déplacement entre les descendants de
<td>
sont filtrés, de sorte queonEnter/Leave
ne fonctionne que si le pointeur quitte ou entre dans<td>
dans son ensemble.
Voici l’exemple complet avec tous les détails:
Résultatscript.jsstyle.cssindex.html// <td> sous la souris actuellement(le cas échéant) let currentElem = null; table.onmouseover = function(event) { // avant d'entrer dans un nouvel élément, la souris quitte toujours le précédent // Si currentElem est défini, nous n'avons pas quitté le précédent <td>, // c'est un mouseover à l'intérieur, ignore l'événement if (currentElem) return; let target = event.target.closest("td"); // nous ne sommes pas passés dans un <td> - ignorer if (!target) return; // déplacé dans <td>, mais en dehors de notre tableau (possible en cas de tableaux imbriquées) // ignorer if (!table.contains(target)) return; // hourra! nous sommes entrés dans un nouveau <td> currentElem = target; onEnter(currentElem); }; table.onmouseout = function(event) { // si nous sommes en dehors de tout <td> maintenant, alors ignorez l'événement // c'est probablement un mouvement à l'intérieur du tableau, mais en dehors des <td>, // par exemple. d'un <tr> à un autre <tr> if (!currentElem) return; // nous quittons l'élément - où aller ? Peut-être à un descendant ? let relatedTarget = event.relatedTarget; while (relatedTarget) { // monte dans la chaîne parente et vérifie - si nous sommes toujours dans currentElem // alors c'est une transition interne - l'ignorer if (relatedTarget == currentElem) return; relatedTarget = relatedTarget.parentNode; } // nous avons quitté le <td>. vraiment. onLeave(currentElem); currentElem = null; }; // any functions to handle entering/leaving an element function onEnter(elem) { elem.style.background = "pink"; // show that in textarea text.value += `over -> ${currentElem.tagName}.${currentElem.className}\n`; text.scrollTop = 1e6; } function onLeave(elem) { elem.style.background = ""; // show that in textarea text.value += `out <- ${elem.tagName}.${elem.className}\n`; text.scrollTop = 1e6; }
#text { display: block; height: 100px; width: 456px; } #table th { text-align: center; font-weight: bold; } #table td { width: 150px; white-space: nowrap; text-align: center; vertical-align: bottom; padding-top: 5px; padding-bottom: 12px; cursor: pointer; } #table .nw { background: #999; } #table .n { background: #03f; color: #fff; } #table .ne { background: #ff6; } #table .w { background: #ff0; } #table .c { background: #60c; color: #fff; } #table .e { background: #09f; color: #fff; } #table .sw { background: #963; color: #fff; } #table .s { background: #f60; color: #fff; } #table .se { background: #0c3; color: #fff; } #table .highlight { background: red; }
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="style.css"> </head> <body> <table id="table"> <tr> <th colspan="3"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th> </tr> <tr> <td class="nw"><strong>Northwest</strong> <br>Metal <br>Silver <br>Elders </td> <td class="n"><strong>North</strong> <br>Water <br>Blue <br>Change </td> <td class="ne"><strong>Northeast</strong> <br>Earth <br>Yellow <br>Direction </td> </tr> <tr> <td class="w"><strong>West</strong> <br>Metal <br>Gold <br>Youth </td> <td class="c"><strong>Center</strong> <br>All <br>Purple <br>Harmony </td> <td class="e"><strong>East</strong> <br>Wood <br>Blue <br>Future </td> </tr> <tr> <td class="sw"><strong>Southwest</strong> <br>Earth <br>Brown <br>Tranquility </td> <td class="s"><strong>South</strong> <br>Fire <br>Orange <br>Fame </td> <td class="se"><strong>Southeast</strong> <br>Wood <br>Green <br>Romance </td> </tr> </table> <textarea id="text"></textarea> <input type="button" onclick="text.value=''" value="Clear"> <script src="script.js"></script> </body> </html>
Essayez de déplacer le curseur dans et hors des cellules du tableau et à l’intérieur de celles-ci. Rapide ou lent – peu importe. Seul
<td>
dans son ensemble est mis en surbrillance, contrairement à l’exemple précédent.Résumé
Nous avons abordé les évènements
mouseover
,mouseout
,mousemove
,mouseenter
etmouseleave
.Ces choses sont bonnes à noter :
- Un mouvement rapide de la souris peut ignorer les éléments intermédiaires.
- Les évènements
mouseover/out
etmouseenter/leave
ont une propriété supplémentaire :relatedTarget
. C’est l’élément duquel nous venons de/à, complémentaire àtarget
.
Les évènements
mouseover/out
se déclenchent même lorsque nous passons de l’élément parent à un élément enfant. Le navigateur suppose que la souris ne peut survoler qu’un seul élément à la fois, le plus profond.Les évènements
mouseenter/leave
sont différents à cet égard : ils ne se déclenchent que lorsque la souris entre et sort de l’élément dans son ensemble. En outre, ils ne “bubble” pas.Exercices
importance: 5Write JavaScript that shows a tooltip over an element with the attribute
data-tooltip
. The value of this attribute should become the tooltip text.That’s like the task Comportement info-bulle, but here the annotated elements can be nested. The most deeply nested tooltip is shown.
Only one tooltip may show up at the same time.
For instance:
<div data-tooltip="Here – is the house interior" id="house"> <div data-tooltip="Here – is the roof" id="roof"></div> ... <a href="https://en.wikipedia.org/wiki/The_Three_Little_Pigs" data-tooltip="Read on…">Hover over me</a> </div>
The result in iframe:
importance: 5Ecrivez une fonction qui montre une info-bulle sur un élément seulement si le visiteur déplace la souris sur celui-ci, et non pas en le traversant.
En d’autres termes, si l’usager déplace la souris sur l’élément et s’arrête dessus – afficher l’info bulle. Et si le visiteur déplace seulement la souris en traversant rapidement l’élément, pas besoin de le faire, qui a besoin d’un clignotement supplémentaire ?
Techniquement, nous pouvons mesurer la vitesse de la souris sur un élément, et si elle est lente alors nous pouvons assumer qu’elle arrive “sur l’élément” et monter l’info-bulle, si elle est rapide – alors on l’ignore.
Créer un objet universel
new HoverIntent(options)
pour cela.Ses
options
:elem
– l’élément à surveiller.over
– une fonction à appeler si la souris arrive sur l’élément : c’est-à-dire qu’elle se déplace lentement ou s’arrête dessus.out
– une fonction à appeler quand la souris quitte l’élément (siover
était appelé).
Un exemple d’usage d’un tel objet pour l’info-bulle:
// un example d'info-bulle let tooltip = document.createElement('div'); tooltip.className = "tooltip"; tooltip.innerHTML = "Tooltip"; // L’objet va suivre la souris et appeler les fonctions over/out new HoverIntent({ elem, over() { tooltip.style.left = elem.getBoundingClientRect().left + 'px'; tooltip.style.top = elem.getBoundingClientRect().bottom + 5 + 'px'; document.body.append(tooltip); }, out() { tooltip.remove(); } });
La demo:
Si vous déplacez la souris rapidement sur la “montre” alors rien ne se passe, et si vous le faites lentement ou que vous vous arrêtez, alors il y aura une info-bulle.
Notez bien: l’info-bulle ne “clignote” pas lorsque le curseur se déplace entre la montre et les sous éléments.
The algorithm looks simple:
- Put
onmouseover/out
handlers on the element. Also can useonmouseenter/leave
here, but they are less universal, won’t work if we introduce delegation. - When a mouse cursor entered the element, start measuring the speed on
mousemove
. - If the speed is slow, then run
over
. - When we’re going out of the element, and
over
was executed, runout
.
But how to measure the speed?
The first idea can be: run a function every
100ms
and measure the distance between previous and new coordinates. If it’s small, then the speed is small.Unfortunately, there’s no way to get “current mouse coordinates” in JavaScript. There’s no function like
getCurrentMouseCoordinates()
.The only way to get coordinates is to listen for mouse events, like
mousemove
, and take coordinates from the event object.So let’s set a handler on
mousemove
to track coordinates and remember them. And then compare them, once per100ms
.P.S. Please note: the solution tests use
dispatchEvent
to see if the tooltip works right. - Sur