Entremos en detalle sobre los eventos que suceden cuando el mouse se mueve entre elementos.
Eventos mouseover/mouseout, relatedTarget
El evento mouseover
se produce cuando el cursor del mouse aparece sobre un elemento y mouseout
cuando se va.
Estos eventos son especiales porque tienen la propiedad relatedTarget
. Esta propiedad complementa a target
. Cuando el puntero del mouse deja un elemento por otro, uno de ellos se convierte en target
y el otro en relatedTarget
.
Para mouseover
:
event.target
– Es el elemento al que se acerca el mouse.event.relatedTarget
– Es el elemento de donde proviene el mouse (relatedTarget
→target
).
Para mouseout
sucede al contrario:
event.target
– Es el elemento que el mouse dejó.event.relatedTarget
– es el nuevo elemento bajo el cursor por cuál el cursor dejó al anterior (target
→relatedTarget
).
En el siguiente ejemplo, cada cara y sus características son elementos separados. Puedes ver en el área de texto los eventos que ocurren cuando mueves el mouse.
Cada evento tiene la información sobre ambas propiedades: target
y 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">¡Los eventos se mostrarán aquí!
</textarea>
<script src="script.js"></script>
</body>
</html>
relatedTarget
puede ser null
La propiedad relatedTarget
puede tener un valor null
.
Eso es normal y solo significa que el mouse no vino de otro elemento, sino de la ventana o que salió de la ventana.
Debemos tener en cuenta esa posibilidad cuando usemos event.relatedTarget
en nuestro código. Si accedemos a event.relatedTarget.tagName
entonces habrá un error.
Saltando elementos
El evento mousemove
se activa cuando el mouse se mueve, pero eso no significa que cada píxel nos lleve a un evento.
El navegador verifica la posición del mouse de vez en cuando y si nota cambios entonces activan los eventos.
Eso significa que si el visitante mueve el mouse muy rápido, entonces algunos elementos del DOM podrían estar siendo ignorados:
Si el mouse se mueve muy rápido de los elementos #FROM
a #TO
, como se muestra arriba, entonces los elementos intermedios <div>
(o algunos de ellos) podrían ser ignorados. El evento mouseout
se podría activar en #FROM
e inmediatamente mouseover
en #TO
.
Eso es bueno para el rendimiento porque puede haber muchos elementos intermedios. Realmente no queremos procesar todo lo que sucede dentro y fuera de cada uno.
Por otro lado, debemos tener en cuenta que el puntero del mouse no “visita” todos los elementos en el camino. Los puede “saltar”.
En particular, es posible que el puntero salte dentro de la mitad de la página desde la ventana. En ese caso relatedTarget
es null
, porque vino de “la nada”:
Puedes verlo “en vivo” en un testeador a continuación.
Este HTML tiene dos elementos: el <div id="child">
está adentro del <div id="parent">
. Si mueves el mouse rápidamente sobre ellos entonces probablemente solo el div
hijo active los eventos, o probablemente el padre, o probablemente no ocurran eventos en lo absoluto.
También prueba a mover el cursor hacia el div
hijo y luego muévelo rápidamente hacia abajo a través del padre. Si el movimiento es lo suficientemente rápido entonces el padre será ignorado. El mouse cruzará el elemento padre sin 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
se activa, deberá haber mouseout
En caso de movimientos rápidos, los elementos intermedios podrían ser ignorados, pero una cosa segura sabemos: si el cursor ingresa “oficialmente” dentro de un elemento(evento mouseover
generado), una vez que lo deje obtendremos mouseout
.
Mouseout, cuando se deja un elemento por uno anidado.
Una característica importante de mouseout
– se activa cuando el cursor se mueve de un elemento hacia su descendiente (elemento anidado o interno). Por ejemplo de #parent
a #child
en este HTML:
<div id="parent">
<div id="child">...</div>
</div>
Si estamos sobre #parent
y luego movemos el cursor hacia dentro de #child
, ¡vamos a obtener mouseout
en #parent
!
Eso puede parecer extraño, pero puede explicarse fácilmente.
De acuerdo con la lógica del navegador, el cursor podría estar sobre un elemento individual en cualquier momento – el anidado y el más alto según el z-index.
Entonces si se dirige hacia otro elemento (incluso uno anidado), está dejando al anterior.
Por favor, note otro importante detalle sobre el procesamiento de eventos.
El evento mouseover
se aparece en un un elemento anidado (brota o nace, por decirlo así). Entonces si #parent
tiene el controlador mouseover
, se activa:
Puedes verlo muy bien a continuación: <div id="child">
está dentro de<div id="parent">
. Hay controladores mouseover/out
en el elemento #parent
que arrojan los detalles de los eventos.
Si mueves el mouse de #parent
a #child
, verás dos eventos sobre #parent
:
mouseout [target: parent]
(dejó al padre), luegomouseover [target: child]
(vino hacia el hijo, y este evento brotó).
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>
Como se muestra, cuando el cursor se mueve del elemento #parent
a #child
, los dos controladores se activan en el elemento padre: mouseout
y mouseover
:
parent.onmouseout = function(event) {
/* event.target: elemento padre */
};
parent.onmouseover = function(event) {
/* event.target: elemento hijo (brota) */
};
Si no examinamos event.target
dentro de los controladores podría parecer que el cursor dejo el elemento #parent
y volvió a él inmediatamente.
Pero ese no es el caso. El cursor aún está sobre el elemento padre, simplemente se adentró más en el elemento hijo.
Si hay algunas acciones al abandonar el elemento padre,por ejemplo: una animación se ejecuta con parent.onmouseout
, usualmente no la queremos cuando el cursor se adentre más sobre #parent
.
Para evitar esto lo que podemos hacer es checar relatedTarget
en el controlador y si el mouse aún permanece dentro del elemento entonces ignorar dicho evento.
Alternativamente podemos usar otros eventos: mouseenter
y mouseleave
, los cuales cubriremos a continuación, ya que con ellos no hay tales problemas.
Eventos mouseenter y mouseleave
Los eventos mouseenter/mouseleave
son como mouseover/mouseout
. Se activan cuando el cursor del mouse entra/sale del elemento.
Pero hay dos diferencias importantes:
- Las transiciones hacia/desde los descendientes no se cuentan.
- Los eventos
mouseenter/mouseleave
no brotan.
Son eventos extremadamente simples.
Cuando el cursor entra en un elemento mouseenter
se activa. La ubicación exacta del cursor dentro del elemento o sus descendientes no importa.
Cuando el cursor deja el elemento mouseleave
se activa.
Este ejemplo es similar al anterior, pero ahora el elemento tiene mouseenter/mouseleave
en lugar de mouseover/mouseout
.
Como puedes ver, los únicos eventos generados son los relacionados con mover el puntero dentro y fuera del elemento superior. No pasa nada cuando el puntero va hacia el descendiente y regresa. Las transiciones entre descendientes se ignoran:
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>
Delegación de eventos
Los eventos mouseenter/leave
son muy simples de usar. Pero no brotan por sí solos. Por lo tanto no podemos usar la delegación de eventos con ellos.
Imagina que queremos manejar entrada/salida para celdas de tabla y hay cientos de celdas.
La solución natural sería: ajustar el controlador en <table>
y manejar los eventos desde ahí. Pero mouseenter/leave
no aparece. Entonces si cada evento sucede en <td>
, solamente un controlador <td>
es capaz de detectarlo.
Los controladores mouseenter/leave
en <table>
solamente se activan cuando el cursor entra/deja la tabla completa. Es imposible obtener alguna información sobre las transiciones dentro de ella.
Pues usemos mouseover/mouseout
.
Comencemos con controladores simples que resaltan el elemento debajo del mouse:
// Resaltemos un elemento debajo del cursor
table.onmouseover = function(event) {
let target = event.target;
target.style.background = 'pink';
};
table.onmouseout = function(event) {
let target = event.target;
target.style.background = '';
};
Aquí se muestran en acción. A medida que el mouse recorre los elementos de esta tabla, se resalta la actual:
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>
En nuestro caso nos gustaría manejar las transiciones entre las celdas de la tabla <td>
: entradas y salidas de una celda a otra. Otras transiciones, como dentro de una celda o fuera de cualquiera de ellas no nos interesan. Vamos a filtrarlas.
Esto es lo que podemos hacer:
- Recordar el elemento
<td>
resaltado actualmente en una variable, llamémoslacurrentElem
. - En
mouseover
ignoraremos el evento si permanecemos dentro del<td>
actual. - En
mouseout
ignoraremos el evento si no hemos dejado el<td>
actual.
Aquí hay un ejemplo de código que explica todas las situaciones posibles:
// Los elementos <td> bajo el maouse justo ahora(si es que hay)
let currentElem = null;
table.onmouseover = function(event) {
// antes de ingresar un uevo elemento, el mouse siempre abandonará al anterior
// si currentElem está establecido, no abandonamos el <td> anterior,
// hay un mouseover dentro de él, ignoramos el evento
if (currentElem) return;
let target = event.target.closest('td');
// si no hay movimientos dentro de un <td> - lo ignoramos
if (!target) return;
//si hay movimientos dentro de un <td>, pero afuera de una tabla(posiblemente en caso de tablas anidadas)
// lo ignoramos
if (!table.contains(target)) return;
// ¡Genial! ingresamos a un nuevo <td>
currentElem = target;
onEnter(currentElem);
};
table.onmouseout = function(event) {
// si estamos afuera de algún <td> ahora, entonces ignoramos el evento
// puede haber movimientos dentro de una tabla, pero fuera de <td>,
// por ejemplo: de un <tr> a otro <tr>
if (!currentElem) return;
// abandonamos el elemento – ¿pero hacia dónde? ¿podría ser hacia un descendiente?
let relatedTarget = event.relatedTarget;
while (relatedTarget) {
// vamos a la cadena de padres y verificamos – si aún estamos dentro de currentElem
// entonces hay una transición interna – la ignoramos
if (relatedTarget == currentElem) return;
relatedTarget = relatedTarget.parentNode;
}
// abandonamos el <td>.
onLeave(currentElem);
currentElem = null;
};
// algunas funciones para manejar entradas/salidas de un elemento
function onEnter(elem) {
elem.style.background = 'pink';
// lo mostramos en el área de texto
text.value += `over -> ${currentElem.tagName}.${currentElem.className}\n`;
text.scrollTop = 1e6;
}
function onLeave(elem) {
elem.style.background = '';
// lo mostramos en el area de texto
text.value += `out <- ${elem.tagName}.${elem.className}\n`;
text.scrollTop = 1e6;
}
Una vez más, las características importantes son:
- Utilizar la delegación de eventos para manejar la entrada/salida de cualquier
<td>
dentro de la tabla. Pues depende demouseover/out
en lugar demouseenter/leave
que no broten y por lo tanto no permita ninguna delegación. - Los eventos adicionales, como moverse entre descendientes de
<td>
son filtrados, así queonEnter/Leave
solamente se ejecuta si el cursor ingresa a<td>
o lo deja absolutamente.
Aquí está el ejemplo completo con todos los detalles:
// Los elementos <td> bajo el maouse justo ahora(si es que hay)
let currentElem = null;
table.onmouseover = function(event) {
// antes de ingresar un uevo elemento, el mouse siempre abandonará al anterior
// si currentElem está establecido, no abandonamos el <td> anterior,
// hay un mouseover dentro de él, ignoramos el evento
if (currentElem) return;
let target = event.target.closest('td');
// si no hay movimientos dentro de un <td> - lo ignoramos
if (!target) return;
//si hay movimientos dentro de un <td>, pero afuera de una tabla(posiblemente en caso de tablas anidadas)
// lo ignoramos
if (!table.contains(target)) return;
// ¡Genial! ingresamos a un nuevo <td>
currentElem = target;
onEnter(currentElem);
};
table.onmouseout = function(event) {
// si estamos afuera de algún <td> ahora, entonces ignoramos el evento
// puede haber movimientos dentro de una tabla, pero fuera de <td>,
// por ejemplo: de un <tr> a otro <tr>
if (!currentElem) return;
// abandonamos el elemento – ¿pero hacia dónde? ¿podría ser hacia un descendiente?
let relatedTarget = event.relatedTarget;
while (relatedTarget) {
// vamos a la cadena de padres y verificamos – si aún estamos dentro de currentElem
// entonces hay una transición interna – la ignoramos
if (relatedTarget == currentElem) return;
relatedTarget = relatedTarget.parentNode;
}
// abandonamos el <td>.
onLeave(currentElem);
currentElem = null;
};
// algunas funciones para manejar entradas/salidas de un elemento
function onEnter(elem) {
elem.style.background = 'pink';
// lo mostramos en el área de texto
text.value += `over -> ${currentElem.tagName}.${currentElem.className}\n`;
text.scrollTop = 1e6;
}
function onLeave(elem) {
elem.style.background = '';
// lo mostramos en el area de texto
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>
Intenta mover el cursor dentro y fuera de las celdas de la tabla y dentro de cada una de ellas. Rápido o lento – no importa --. Solo se ilumina <td>
como un todo, a diferencia del ejemplo anterior.
Resumen
Hemos cubierto mouseover
, mouseout
, mousemove
, mouseenter
ymouseleave
.
Estas cosas son buenas de destacar:
- Un movimiento rápido del mouse puede omitir elementos intermedios.
- Los eventos
mouseover/out
ymouseenter/leave
tienen una propiedad adicional:relatedTarget
. Es el elemento de donde venimos o hacia donde vamos, complementario contarget
.
Los eventos mouseover/out
se activan incluso cuando vamos de un elemento padre a su descendiente. El navegador asume que de el mouse solo puede estar sobre un elemento a la vez – el más interno.
Los eventos mouseenter/leave
son diferentes en ese aspecto: solo se activan cuando el mouse viene hacia el elemento o lo deja como un todo. Así que no se aparecen de repente.