jQuery / Método / .hover() - Entrada, salida y alternativas con on
En jQuery, .hover() es un atajo para registrar funciones cuando el puntero entra y sale de un elemento. Está obsoleto desde jQuery 3.3; para código nuevo usa .on("mouseenter", ...) y .on("mouseleave", ...). Obsoleto no significa eliminado: ambos enfoques siguientes se ejecutaron con 3.7.1 y 4.0.0.
Dos funciones o una función compartida
La forma de dos funciones de .hover() llama a la primera al entrar y a la segunda al salir. Registra mouseenter y mouseleave, no un evento llamado hover.
| Forma anterior | Sustitución | Significado |
|---|---|---|
| .hover(enter, leave) | .on("mouseenter", enter).on("mouseleave", leave) | Funciones separadas para entrada y salida |
| .hover(handler) | .on("mouseenter mouseleave", handler) | Una función para ambos eventos |
mouseenter trata del límite del elemento. Si el puntero ya está dentro del padre y pasa a su span hijo, no vuelve a entrar en el padre. Se diferencia de mouseover propagado desde transiciones de hijos, evitando esas repeticiones no deseadas.
Ejemplo completo de hover y on
Guarda el código completo en UTF-8 como hover-demo.html y ábrelo en el navegador. Hace falta internet para cargar el archivo oficial de jQuery 4.0.0 desde su red de distribución de contenidos, CDN, sin instalar paquetes. Ambos ejemplos, transiciones de hijos, elementos dinámicos, teclado y retirada se ejecutaron con 3.7.1 y 4.0.0.
Alpha usa .hover() y Beta .on() delegado desde #cards. El contenido siempre está visible y solo cambia el resaltado. Beta también trata focusin/focusout para el foco de teclado.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hover events and keyboard focus</title>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<style>
body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 24px auto; padding: 16px; }
button { font: inherit; margin: 6px; }
.card { display: block; padding: 20px; border: 2px solid teal; background: white; }
.active { background: #d9f4ef; }
.card:focus-visible { outline: 3px solid #af5f00; outline-offset: 3px; }
</style>
</head>
<body>
<h1>Compare legacy hover and on</h1>
<button type="button" id="add">Add modern card</button>
<button type="button" id="bind">Bind modern</button>
<button type="button" id="unbind">Unbind modern</button>
<button type="button" id="reset">Reset</button>
<div id="cards">
<button type="button" class="card legacy">Legacy <span>Alpha</span></button>
<button type="button" class="card modern">Modern <span>Beta</span></button>
</div>
<p id="result" aria-live="polite"></p>
<script>
$(function () {
const $cards = $("#cards");
let legacyEntries = 0;
let modernEntries = 0;
function report() {
$("#result").text(`legacy enters=${legacyEntries} | modern enters=${modernEntries}`);
}
$(".legacy").hover(function () {
legacyEntries++;
$(this).addClass("active");
report();
}, function () { $(this).removeClass("active"); });
function bindModern() {
$cards.off(".hoverLesson").on(
"mouseenter.hoverLesson mouseleave.hoverLesson focusin.hoverLesson focusout.hoverLesson",
".modern", function (event) {
const $card = $(this);
if (event.type === "mouseenter") { $card.data("inside", true); modernEntries++; }
if (event.type === "mouseleave") $card.data("inside", false);
if (event.type === "focusin") $card.data("focused", true);
if (event.type === "focusout") $card.data("focused", false);
$card.toggleClass("active", Boolean($card.data("inside") || $card.data("focused")));
report();
}
);
}
$("#bind").on("click", bindModern);
$("#unbind").on("click", function () {
$cards.off(".hoverLesson");
$(".modern").removeClass("active").removeData("inside focused");
});
$("#add").on("click", function () {
if (!$("#gamma").length) $('<button type="button" id="gamma" class="card modern">Modern <span>Gamma</span></button>').appendTo($cards);
});
$("#reset").on("click", function () {
$("#gamma").remove();
$(".card").removeClass("active").removeData("inside focused");
legacyEntries = 0;
modernEntries = 0;
bindModern();
report();
});
bindModern();
report();
});
</script>
</body>
</html>
Ambos contadores de entrada empiezan en cero:
legacy enters=0 | modern enters=0
- Entra en Legacy Alpha. Cambia el fondo y legacy enters aumenta una vez. Pasar a su span Alpha interno no lo incrementa de nuevo.
- Sal para quitar el resaltado. Modern Beta aumenta modern enters por cada entrada.
- Add modern card crea Gamma. El manejador delegado del padre procesa la nueva tarjeta sin registrarla aparte.
- Usa Tab para enfocar Beta. Aparecen fondo y contorno. Si solo sale el puntero, el fondo permanece mientras conserve foco; deben salir ambos para quitarlo.
- Unbind modern desactiva solo Modern. El hover de Alpha sigue funcionando.
- Repetir Bind modern no duplica llamadas. Reset elimina Gamma y resaltados, pone contadores a cero y vuelve a conectar Modern.
inside y focused mantienen por separado puntero y foco. Basta uno para conservar el resaltado. Quitar la clase siempre en mouseleave borraría la señal visual aunque siga el foco de teclado.
Elementos dinámicos y alcance de la retirada
La forma delegada de .on() se conecta a un padre estable con un selector de descendientes. Aquí #cards permanece y solo trata botones .modern. .hover() se conecta directamente a la selección existente al registrarse; los elementos futuros no heredan automáticamente el manejador.
hoverLesson es un espacio de nombres de eventos. bindModern() llama primero a .off(".hoverLesson") para quitar solo sus propios manejadores y después registrarlos otra vez. .off() sin argumentos o .off("mouseenter mouseleave") demasiado amplio podría retirar los de otros componentes.
En una function normal, this es la tarjeta Modern procesada. No equivale a event.target, que puede ser el span interno donde se originó el evento. No cambies sin más a una función flecha si necesitas ese this.
Función única y opciones de entrada
Ejecuta este fragmento completo en Console de las herramientas de desarrollo para observar ambos tipos en una función. Dispara eventos en un botón temporal sin insertarlo y lo limpia después. El movimiento real se observa en el ejemplo completo.
const events = [];
const $button = $("<button>Temporary</button>");
$button.hover(function (event) { events.push(event.type); });
$button.trigger("mouseenter").trigger("mouseleave");
console.log(events.join(", "));
$button.remove();
mouseenter, mouseleave
Son entradas y salidas, no seguimiento continuo de cada movimiento. Comprobar event.type y asignar un estado explícito evita que un toggle simple quede invertido tras otros cambios de estado.
No dependas solo del hover del ratón para revelar contenido esencial en pantallas táctiles. Ofrece clic/toque y teclado para funciones importantes. Para decoración pueden bastar CSS :hover y :focus-visible. Este ejemplo siempre muestra el contenido y solo cambia su resaltado.




