マウスを要素に乗せたときのホバーエフェクト、マウス座標に追従するツールチップ、ドロップダウンメニューの表示など、マウスイベントは Web サイトのインタラクションの基本です。CSS の :hover だけでは実現できない動的な処理を、JavaScript のマウスイベントで実装します。
・mouseenter / mouseleave と mouseover / mouseout の違い
・mousemove でマウス座標を取得する方法
・event オブジェクトの座標プロパティ(clientX / offsetX / pageX)
・CSS :hover と JavaScript マウスイベントの使い分け
・Pointer Events でタッチ・ペンデバイスにも対応する方法
・ツールチップ・ドロップダウン・画像プレビュー・マウス追従エフェクトの実務パターン
マウスイベントの種類
| イベント | 発火タイミング | バブリング |
|---|---|---|
mouseenter |
マウスが要素に入ったとき | × しない |
mouseleave |
マウスが要素から出たとき | × しない |
mouseover |
マウスが要素に入ったとき | ○ する |
mouseout |
マウスが要素から出たとき | ○ する |
mousemove |
マウスが要素上で動いている間 | ○ する |
mousedown |
マウスボタンを押したとき | ○ する |
mouseup |
マウスボタンを離したとき | ○ する |
mouseenter / mouseleave と mouseover / mouseout の違い
最も重要な違いはバブリングの有無です。mouseover は子要素に移動したときにも発火しますが、mouseenter は要素自身に入ったときだけ発火します。
<div id="parent"> <span id="child">子要素</span> </div>
const parent = document.getElementById("parent");
parent.addEventListener("mouseover", () => {
console.log("mouseover"); // 親に入ったとき + 子要素に移動したときにも発火
});
parent.addEventListener("mouseout", () => {
console.log("mouseout"); // 子要素に移動したときにも発火
});
const parent = document.getElementById("parent");
parent.addEventListener("mouseenter", () => {
console.log("mouseenter"); // 親に入ったときだけ発火
});
parent.addEventListener("mouseleave", () => {
console.log("mouseleave"); // 親から完全に出たときだけ発火
});
| 比較項目 | mouseenter / mouseleave | mouseover / mouseout |
|---|---|---|
| バブリング | しない | する |
| 子要素への移動 | 発火しない | 発火する(意図しない動作の原因に) |
| イベント委任 | 使えない | 使える(バブリングが必要) |
| 推奨場面 | ホバーエフェクト・ツールチップ | イベント委任が必要な場面 |
mouseenter / mouseleave を使ってください。子要素がある要素で mouseover / mouseout を使うと、子要素への移動のたびに余計な発火が起きます。マウス座標を取得する
event オブジェクトには複数の座標プロパティがあり、基準点が異なります。
| プロパティ | 基準点 | 用途 |
|---|---|---|
clientX / clientY |
ビューポートの左上 | ツールチップの位置計算 |
pageX / pageY |
ページ全体の左上 | スクロール量を含む絶対位置 |
offsetX / offsetY |
対象要素の左上 | Canvas の描画座標 |
screenX / screenY |
画面(モニター)の左上 | マルチモニター判定(ほぼ使わない) |
const box = document.getElementById("box");
box.addEventListener("mousemove", (e) => {
console.log(`client: (${e.clientX}, ${e.clientY})`); // ビューポート基準
console.log(`page: (${e.pageX}, ${e.pageY})`); // ページ基準
console.log(`offset: (${e.offsetX}, ${e.offsetY})`); // 要素基準
});
mousemove はマウスが動いている間毎秒数十回発火します。重い処理を行う場合は throttle を適用してください。CSS :hover と JavaScript の使い分け
| やりたいこと | CSS :hover | JavaScript |
|---|---|---|
| 背景色・文字色の変更 | ○ 推奨 | 不要 |
| 要素の表示 / 非表示 | ○ 簡単な場合 | 遅延や条件付きの場合 |
| アニメーション | ○ transition / animation | 複雑な制御が必要な場合 |
| DOM の変更(テキスト差し替えなど) | × | ○ 必須 |
| データ取得(API 呼び出しなど) | × | ○ 必須 |
| ツールチップ(座標追従) | △ CSS のみでは位置が固定 | ○ mousemove で追従 |
.card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
:hover が最も効率的です。JavaScript は DOM 操作・データ取得・座標追従など、CSS では実現できない処理に使いましょう。Pointer Events でタッチ・ペンにも対応する
マウスイベントはマウス専用ですが、Pointer Events はマウス・タッチ・ペンを統一的に扱えるAPI です。
| Mouse Event | Pointer Event | 対応するデバイス |
|---|---|---|
mouseenter |
pointerenter |
マウス + タッチ + ペン |
mouseleave |
pointerleave |
マウス + タッチ + ペン |
mousemove |
pointermove |
マウス + タッチ + ペン |
mousedown |
pointerdown |
マウス + タッチ + ペン |
const card = document.getElementById("card");
// Pointer Events: マウスでもタッチでも動作
card.addEventListener("pointerenter", () => {
card.classList.add("is-hovered");
});
card.addEventListener("pointerleave", () => {
card.classList.remove("is-hovered");
});
// デバイスの種類を判別
card.addEventListener("pointerdown", (e) => {
console.log(e.pointerType); // "mouse" / "touch" / "pen"
});
Pointer Events が推奨です。IE11 以降のすべてのモダンブラウザで対応しています。実務でよく使うパターン
ツールチップ(マウス座標に追従)
const tooltip = document.getElementById("tooltip");
document.querySelectorAll("[data-tooltip]").forEach(el => {
el.addEventListener("mouseenter", () => {
tooltip.textContent = el.getAttribute("data-tooltip");
tooltip.style.display = "block";
});
el.addEventListener("mousemove", (e) => {
tooltip.style.left = (e.clientX + 12) + "px";
tooltip.style.top = (e.clientY + 12) + "px";
});
el.addEventListener("mouseleave", () => {
tooltip.style.display = "none";
});
});
ドロップダウンメニュー(遅延付き)
const menuItem = document.querySelector(".menu-item");
const dropdown = menuItem.querySelector(".dropdown");
let hideTimer = null;
menuItem.addEventListener("mouseenter", () => {
clearTimeout(hideTimer);
dropdown.style.display = "block";
});
menuItem.addEventListener("mouseleave", () => {
// 300ms の遅延で閉じる(マウスがメニューに戻れば取り消し)
hideTimer = setTimeout(() => {
dropdown.style.display = "none";
}, 300);
});
画像のホバープレビュー
document.querySelectorAll(".thumbnail").forEach(thumb => {
thumb.addEventListener("mouseenter", () => {
const mainImg = document.getElementById("mainImage");
mainImg.src = thumb.getAttribute("data-full-src");
mainImg.alt = thumb.alt;
});
});
マウス追従エフェクト(カーソルカスタマイズ)
.cursor-dot {
position: fixed;
width: 12px;
height: 12px;
background: #0284c7;
border-radius: 50%;
pointer-events: none;
transition: transform 0.1s ease;
z-index: 9999;
}
const dot = document.querySelector(".cursor-dot");
document.addEventListener("mousemove", (e) => {
dot.style.left = e.clientX + "px";
dot.style.top = e.clientY + "px";
});
関連記事
- クリックイベントの設定方法 — addEventListener・バブリング・イベント委任
- スクロールイベントの使い方
- classList の使い方完全ガイド
- getAttribute の使い方
- setTimeout の使い方 — 遅延処理
- マウスオーバーで新規要素を追加する
よくある質問
mouseenter / mouseleave を使ってください。mouseover / mouseout はバブリングするため、子要素への移動で余計に発火します。イベント委任(親要素でまとめて処理)が必要な場合のみ mouseover を使います。:hover が最適です。JavaScript は DOM 操作(テキスト差し替え)、データ取得(API)、座標追従(ツールチップ)など CSS では実現できない処理に使います。Pointer Events(pointerenter / pointerleave 等)を使うか、touchstart / mouseenter の両方を登録してください。throttle や requestAnimationFrame で実行回数を制限してください。座標の表示やカーソルドットの移動程度であれば、通常パフォーマンス問題は発生しません。mouseleave で即座に閉じるのではなく、setTimeout で 200〜300ms の遅延を入れてください。マウスがメニューに戻った場合は clearTimeout で閉じる処理をキャンセルします。まとめ
JavaScript のマウスイベントの使い方を整理しました。
- ホバー検出:
mouseenter / mouseleave(バブリングしない、推奨) - 座標取得:
mousemoveのclientX / clientY(ビューポート基準) - CSS との使い分け: スタイル変更は CSS :hover、DOM 操作・座標追従は JavaScript
- タッチ対応:
Pointer Events(pointerenter 等)で統一 - パフォーマンス: mousemove には throttle / rAF を適用
ツールチップやドロップダウンメニューなど、マウス操作に応じた UI はユーザー体験を大きく向上させます。CSS :hover で済む場面と JavaScript が必要な場面を使い分けて、効率的に実装しましょう。