Entriamo nel dettaglio degli eventi generati quando il mouse si sposta tra gli elementi.
Eventi mouseover/mouseout, relatedTarget
L’evento mouseover
viene generato quando il puntatore del mouse passa su un elemento, e mouseout
– quando lo abbandona.
Sono eventi particolari, perché posseggono la proprietà relatedTarget
. Questa proprietà è complementare rispetto a target
. Quando il mouse passa da un elemento a un altro, uno di questi diventa il target
, e l’altro – relatedTarget
.
Per mouseover
:
event.target
– é l’elemento appena raggiunto dal mouse.event.relatedTarget
– è l’elemento appena abbandonato dal mouse (relatedTarget
→target
).
Per mouseout
, invece, è esattamente il contrario:
event.target
– è l’elemento appena lasciato dal mouse.event.relatedTarget
– è il nuovo elemento sotto il puntatore (target
→relatedTarget
).
Nel seguente esempio, ogni faccia e le sue proprietà sono elementi separati. Al movimento del mouse, corrispondono degli eventi che vengono descritti nell’area di testo.
Ogni evento contiene entrambe le informazioni sia del target
che del 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">Events will show up here!
</textarea>
<script src="script.js"></script>
</body>
</html>
relatedTarget
può essere null
La proprietà relatedTarget
può essere null
.
È normale e significa solo che il mouse non proviene da un altro elemento della UI, ma esternamente rispetto alla finestra. Oppure può significare che l’ha appena lasciata (per l’evento mouseover
).
Dobbiamo tenere a mente questa eventualità, quando coinvolgiamo event.relatedTarget
nel nostro codice, perché in queste condizioni, nel tentativo di accedere ad event.relatedTarget.tagName
, andremmo incontro ad un errore.
Saltare elementi
L’evento mousemove
viene attivato dal movimento del mouse. Tuttavia, ciò non significa che ogni pixel porterà ad un evento.
Il browser controlla la posizione del mouse di tanto in tanto. E se in questo frangente noterà qualche cambiamento, allora genererà degli eventi.
Ne consegue che, se l’utente muovesse il mouse molto velocemente, potrebbero essere “saltati” alcuni elementi del DOM:
Se il mouse si muovesse molto velocemente, passando dagli elementi #FROM
a #TO
appena illustrati, gli elementi <div>
intermedi (o alcuni di essi) potrebbero essere ignorati. L’evento mouseout
potrebbe essere generato su #FROM
ed il successivo mouseover
immediatamente su #TO
.
Questo è sicuramente ottimo in termini di prestazioni, dal momento che potrebbero esserci tanti elementi intermedi. Non vogliamo veramente elaborare l’entrata e uscita per ognuno di essi.
D’altra parte, dovremmo anche tenere a mente che il puntatore del mouse non “visita” tutti gli elementi lungo il suo cammino. Può appunto “saltare” elementi.
In particolare, è possibile che il puntatore arrivi direttamente al centro della pagina, provenendo dall’esterno della finestra. In questo caso relatedTarget
sarebbe null
, non venendo da “nessuna parte”:
Possiamo testare dal “vivo” il concetto, nel seguente banco di prova.
Questo HTML ha due elementi nidificati: un <div id="child">
dentro un <div id="parent">
. Muovendo il mouse velocemente su di loro, potrebbe accadere che l’evento venga generato solo dal div figlio, o magari solo dal genitore, oppure ancora, potrebbe non esserci alcun evento.
Inoltre, si potrebbe provare a spostare il puntatore dentro il div
figlio, e poi subito dopo, muoverlo velocemente attraverso il genitore. Se il movimento è abbastanza veloce, allora l’elemento genitore potrebbe essere ignorato. In questo caso, il mouse attraverserebbe l’elemento genitore senza nemmeno “notarlo”.
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
, allora deve esserci mouseout
Come appena detto, per movimenti sufficientemente rapidi, gli elementi intermedi potrebbero essere ignorati, ma una cosa è certa: se il puntatore entra “ufficialmente” dentro un elemento (quindi è stato generato l’evento mouseover
), allora dopo averlo lasciato otterremo sempre un mouseout
.
Mouseout quando si abbandona il genitore per un elemento figlio
Un’importante caratteristica di mouseout
è che – viene generato quando il puntatore si muove da un elemento verso un suo discendente, ad esempio da #parent
verso #child
come nel seguente HTML:
<div id="parent">
<div id="child">...</div>
</div>
Se siamo su #parent
e spostiamo il mouse del tutto dentro #child
, otteniamo un mouseout
on #parent
!
A prima vista può sembrare strano, ma la spiegazione è molto semplice.
Coerentemente con la logica del browser, il puntatore del mouse può stare sopra un solo elemento per volta – il più annidato e con il valore di z-index più alto.
Quindi, se il puntatore si sposta su un altro elemento (anche nel caso di un suo discendente), allora lascia il precedente.
È bene porre la nostra attenzione ad un importante dettaglio sull’elaborazione dell’evento.
L’evento mouseover
su un discendente “sale verso l’alto” (bubbling). Quindi, se #parent
avesse un gestore mouseover
, questo verrebbe attivato:
Possiamo notare il fenomeno palesarsi nel seguente esempio: <div id="child">
è dentro <div id="parent">
. Abbiamo dei gestori mouseover/out
sull’elemento #parent
che generano dettagli sugli eventi.
Spostando il mouse da #parent
a #child
, è possibile notare due eventi su #parent
:
mouseout [target: parent]
(indica che ha lasciato il genitore), e poimouseover [target: child]
(ci dice che è arrivato sul figlio, con il bubbling).
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>
Si nota bene che quando il puntatore si muove dall’elemento #parent
al #child
, vengono attivati due gestori sull’elemento genitore: mouseout
e mouseover
:
parent.onmouseout = function(event) {
/* event.target: elemento genitore */
};
parent.onmouseover = function(event) {
/* event.target: elemento figlio (bubbled) */
};
Se non analizzassimo event.target
dentro i gestori, potremmo essere portati a pensare che il puntatore lasci l’elemento genitore per poi di nuovo rientrarci subito dopo.
Ma non è così. Il puntatore è ancora sul genitore, solo che si è mosso più internamente, dentro l’elemento figlio.
Se vi fossero degli eventi generati dall’abbandono dell’elemento genitore, ad esempio un’animazione eseguita al parent.onmouseout
, nella maggioranza dei casi non vorremmo che si attivassero se il puntatore entrasse in profondità nel #parent
andando dentro il figlio (anzi, in generale vorremmo che si attivassero solo quando il puntatore va all’esterno dell’area del #parent
).
Quindi, per evitare questo, possiamo controllare relatedTarget
nel gestore, e se il mouse è ancora dentro l’elemento, ignoriamo del tutto l’evento.
In alternativa, possiamo usare altri eventi: mouseenter
e mouseleave
, che affronteremo proprio adesso, che non sono affetti da queste problematiche.
Eventi mouseenter e mouseleave
Gli eventi mouseenter/mouseleave
si comportano come mouseover/mouseout
. Vengono attivati quando il puntatore del mouse entra/lascia l’elemento.
Ma hanno due importanti differenze:
- Le transizioni dentro l’elemento, da e verso i discendenti, non vengono considerate.
- Gli eventi
mouseenter/mouseleave
non “risalgono” in bubbling.
Sono eventi estremamente semplici.
Quando il puntatore entra su un elemento – viene generato mouseenter
. La posizione esatta del puntatore dentro l’elemento o dei suoi discendenti è del tutto irrilevante.
Quando il puntatore lascia un elemento – viene generato mouseleave
.
L’esempio seguente è simile al precedente, solo che in questo caso l’elemento superiore è associato a mouseenter/mouseleave
piuttosto che mouseover/mouseout
.
Come possiamo notare, gli unici eventi generati sono quelli relativi al movimento del puntatore del mouse dentro e fuori dall’elemento superiore. Se il puntatore va dentro l’elemento figlio, non succede nulla. Le transizioni tra i figli vengono ignorate.
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>
Event delegation
Gli eventi mouseenter/leave
sono molto semplici e facili da usare. Ma, abbiamo detto, non seguono la logica del bubbling. Quindi, con questi eventi non potremo mai usare la event delegation.
Ora immaginiamo di voler gestire il movimento del mouse, sia in entrata che in uscita dalle celle di una tabella. E immaginiamo anche che questa tabella abbia centinaia di celle.
La soluzione più naturale sarebbe quella di – impostare un gestore su <table>
per elaborare lì gli eventi. Ma il problema è che mouseenter/leave
non fanno bubbling. Quindi se questi eventi avvengono su <td>
, solo un gestore su <td>
potrà catturarli.
I gestori per mouseenter/leave
sulla <table>
verrebbero generati solo se il puntatore entrasse ed uscisse dalla tabella, e sarebbe impossibile ottenere informazioni sugli spostamenti al suo interno.
Quindi, saremmo costretti ad usare mouseover/mouseout
.
Cominciamo con dei semplici gestori che evidenziano gli elementi sotto il mouse:
// evidenziamo un elemento sotto il puntatore
table.onmouseover = function(event) {
let target = event.target;
target.style.background = 'pink';
};
table.onmouseout = function(event) {
let target = event.target;
target.style.background = '';
};
Eccoli in azione. Quando il mouse si sposta attraverso gli elementi di questa tabella, vengono evidenziati:
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>
Nel nostro caso vogliamo gestire i passaggi tra le celle della tabella <td>
: cioè quando entra in una cella lasciandone un’altra. Gli altri passaggi, come quelli all’interno o fuori da ogni altra celle, non ci interessano e li filtriamo.
Ecco come possiamo fare:
- Memorizzare l’attuale
<td>
evidenziata in una variabile, che chiameremocurrentElem
. - Al
mouseover
– ignorarlo se siamo ancora dentro l’elemento<td>
corrente. - Al
mouseout
– ignorarlo se non abbiamo lasciato il<td>
corrente.
Ecco un esempio di codice che tiene conto di tutte le combinazioni:
// <td> sotto il mouse proprio adesso (ove previsto)
let currentElem = null;
table.onmouseover = function (event) {
// prima di entrare su un nuovo elemento, il mouse abbandona quello precedente
// se currentElem e' impostato, non abbiamo abbandonato il precedente <td>,
// il mouse è ancora dentro, ignoriamo l'evento
if (currentElem) return;
let target = event.target.closest('td');
// ci siamo spostati ma non dentro un td <td> - ignoriamo
if (!target) return;
// ci siamo spostati dentro un <td>, ma fuori dalla nostra tabella (possibile in casi di tabelle annidate)
// ignoriamo
if (!table.contains(target)) return;
// evviva! siamo entrati dentro un nuovo <td>
currentElem = target;
onEnter(currentElem);
};
table.onmouseout = function (event) {
// se adesso siamo fuori da qualunque <td>, ignoriamo l'evento
// si tratta probabilmente di un movimento dentro la tabella, ma fuori dal <td>,
// ad esempio da un <tr> a un altro <tr>
if (!currentElem) return;
// stiamo abbandonando l'elemento – verso dove? Forse un nodo discendente?
let relatedTarget = event.relatedTarget;
while (relatedTarget) {
// risale la "catena" dei nodi genitori e controlla - se siamo ancora dentro currentElem
// si tratta di uno spostamento interno - lo ignoriamo
if (relatedTarget == currentElem) return;
relatedTarget = relatedTarget.parentNode;
}
// abbiamo lasciato il <td>. per davvero.
onLeave(currentElem);
currentElem = null;
};
// qualsiasi funzione per gestire l'entrata e l'uscita da un elemento
function onEnter(elem) {
elem.style.background = 'pink';
// lo mostra nella textarea
text.value += `over -> ${currentElem.tagName}.${currentElem.className}\n`;
text.scrollTop = 1e6;
}
function onLeave(elem) {
elem.style.background = '';
// lo mostra nella textarea
text.value += `out <- ${elem.tagName}.${elem.className}\n`;
text.scrollTop = 1e6;
}
Ancora una volta, le caratteristiche da tenere in considerazione sono:
- Usare la event delegation per gestire l’entrata/uscita dai
<td>
dentro la tabella. Quindi usaremouseover/out
piuttosto chemouseenter/leave
, in quanto questi ultimi, non facendo bubbling, non permetterebbero la event delegation. - Gli eventi aggiuntivi, come lo spostamento del mouse tra gli elementi discendenti di
<td>
vanno esclusi, in modo da eseguireonEnter/Leave
solo quando il puntatore entra o abbandona del tutto il<td>
.
Ecco un esempio con tutti i dettagli:
// <td> sotto il mouse proprio adesso (ove previsto)
let currentElem = null;
table.onmouseover = function (event) {
// prima di entrare su un nuovo elemento, il mouse abbandona quello precedente
// se currentElem e' impostato, non abbiamo abbandonato il precedente <td>,
// il mouse è ancora dentro, ignoriamo l'evento
if (currentElem) return;
let target = event.target.closest('td');
// ci siamo spostati ma non dentro un td <td> - ignoriamo
if (!target) return;
// ci siamo spostati dentro un <td>, ma fuori dalla nostra tabella (possibile in casi di tabelle annidate)
// ignoriamo
if (!table.contains(target)) return;
// evviva! siamo entrati dentro un nuovo <td>
currentElem = target;
onEnter(currentElem);
};
table.onmouseout = function (event) {
// se adesso siamo fuori da qualunque <td>, ignoriamo l'evento
// si tratta probabilmente di un movimento dentro la tabella, ma fuori dal <td>,
// ad esempio da un <tr> a un altro <tr>
if (!currentElem) return;
// stiamo abbandonando l'elemento – verso dove? Forse un nodo discendente?
let relatedTarget = event.relatedTarget;
while (relatedTarget) {
// risale la "catena" dei nodi genitori e controlla - se siamo ancora dentro currentElem
// si tratta di uno spostamento interno - lo ignoriamo
if (relatedTarget == currentElem) return;
relatedTarget = relatedTarget.parentNode;
}
// abbiamo lasciato il <td>. per davvero.
onLeave(currentElem);
currentElem = null;
};
// qualsiasi funzione per gestire l'entrata e l'uscita da un elemento
function onEnter(elem) {
elem.style.background = 'pink';
// lo mostra nella textarea
text.value += `over -> ${currentElem.tagName}.${currentElem.className}\n`;
text.scrollTop = 1e6;
}
function onLeave(elem) {
elem.style.background = '';
// lo mostra nella 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>
Proviamo a spostare il cursore dentro e fuori dalla celle della tabella ed anche al loro interno. Velocemente o lentamente – è irrilevante. Solo l’intero <td>
deve essere evidenziato, diversamente da quanto fatto nell’esempio precedente.
Riepilogo
Abbiamo visto gli eventi mouseover
, mouseout
, mousemove
, mouseenter
e mouseleave
.
Queste sono le cose da evidenziare:
- Un rapido movimento del mouse può fare ignorare gli elementi intermedi.
- Gli eventi
mouseover/out
emouseenter/leave
posseggono una proprietà aggiuntiva:relatedTarget
sarà l’elemento dal quale stiamo uscendo, o nel quale stiamo entrando, ed è complementare atarget
.
Gli eventi mouseover/out
vengono generati anche quando andiamo dall’elemento genitore all’elemento figlio. Il browser assume che il mouse può stare su un solo elemento alla volta – quello più annidato.
Gli eventi mouseenter/leave
sono differenti da questo punto di vista: vengono scaturiti solo quando il mouse entra o esce del tutto da un elemento. Inoltre non sono soggetti al bubbling.
Commenti
<code>
, per molte righe – includile nel tag<pre>
, per più di 10 righe – utilizza una sandbox (plnkr, jsbin, codepen…)