jQuery / メソッド / .hover() - 進入・退出イベントとonへの置き換え
jQueryの.hover()は、マウスポインターが要素に入るときと出るときの関数を登録する省略メソッドです。jQuery 3.3から非推奨で、新しいコードには.on("mouseenter", ...)と.on("mouseleave", ...)を使います。非推奨と削除は異なり、以下の両方式は3.7.1と4.0.0で実行しました。
二つの関数と一つの共通関数
.hover()の二関数形式は、最初を進入時、二つ目を退出時に呼びます。hoverというイベントを登録するのではなく、mouseenterとmouseleaveをまとめて扱います。
| 従来の形式 | 置き換え | 意味 |
|---|---|---|
| .hover(enter, leave) | .on("mouseenter", enter).on("mouseleave", leave) | 進入と退出を別関数で処理 |
| .hover(handler) | .on("mouseenter mouseleave", handler) | 両イベントを同じ関数で処理 |
mouseenterは要素の境界への進入です。既に親の中にあるポインターが子のspanへ移っても、親への再進入にはなりません。子への移動でも伝播するmouseoverとの違いを理解すると、不要な繰り返しを防げます。
hoverとonを比較する完全な例
全コードをUTF-8のhover-demo.htmlとして保存し、ブラウザーで開きます。コンテンツ配信ネットワーク(CDN)から公式jQueryファイル4.0.0を読むためインターネット接続が必要ですが、インストールは不要です。両例、子への移動、動的要素、キーボード、解除を3.7.1と4.0.0で実行しました。
Alphaは従来の.hover()、Betaは#cardsに委任した.on()を使います。カードの内容は常に表示し、強調だけを変えます。Betaにはfocusin・focusoutも登録し、キーボードフォーカスでも強調します。
<!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>
初期の進入回数はどちらも0です。
legacy enters=0 | modern enters=0
- Legacy Alphaに入ると背景が変わり、legacy entersが一度増えます。内部のAlphaのspanへ動いても増えません。
- 外へ出ると強調が消えます。Modern Betaも進入ごとにmodern entersが増えます。
- Add modern cardでGammaを追加します。親の委任処理が新しいカードも扱い、個別登録なしで動作します。
- TabでBetaにフォーカスすると背景と外枠が現れます。ポインターだけ外へ動かしてもフォーカスが残る間は背景を維持し、両方が外れると解除します。
- Unbind modernはModernだけを解除し、Alphaのhoverは動き続けます。
- Bind modernを繰り返しても重複呼び出しになりません。ResetはGammaと強調を消し、回数を0にし、Modernを再登録します。
ポインターとフォーカスをinside・focusedとして別々に保持し、片方でも有効なら強調します。mouseleaveで無条件にクラスを消すと、キーボードフォーカスが残っていても視覚的な状態が失われます。
動的要素と解除範囲
.on()の委任形式は残り続ける親に登録し、子孫セレクターを指定します。ここでは#cardsが残り、.modernボタンだけを扱います。従来の.hover()は登録時の選択要素に直接接続するため、後から作った要素が自動的に同じ処理を得るとは限りません。
hoverLessonはイベント名前空間です。bindModern()は.off(".hoverLesson")で自分の処理だけを外してから再登録します。引数なしの.off()や広範囲の.off("mouseenter mouseleave")は他のコードの処理も消しかねないため、範囲を限定します。
通常のfunction内のthisは処理対象のModernカードです。イベントの発生元となった内部spanかもしれないevent.targetとは区別します。このthisを必要とするなら、安易にアロー関数へ変更しません。
単一関数形式と入力方法を確認する
開発者ツールのConsoleで次の完全な部分コードを実行すると、一つの関数に両イベントが渡ることを確認できます。一時ボタンを挿入せずにイベントを発生させ、最後に整理します。実際のポインター移動は上の完全な例で確認します。
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
これは進入・退出を受けるという意味で、マウスの動きを常時受けるわけではありません。共通関数でもevent.typeに応じて状態を明示すると、他の変更で単純なtoggleの状態が逆になる問題を避けやすくなります。
タッチ画面ではhoverだけで必須内容を開く設計に頼りません。重要な機能はクリック・タップやキーボードでも使えるようにし、装飾だけならCSSの:hoverと:focus-visibleで十分かも検討します。この例は内容を常に表示し、強調だけを変えます。




