【JavaScript】マウスイベントの使い方|mouseenter・mouseleave・mouseover・mousemove・CSS :hover との使い分けまで解説

マウスを要素に乗せたときのホバーエフェクト、マウス座標に追従するツールチップ、ドロップダウンメニューの表示など、マウスイベントは 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 は要素自身に入ったときだけ発火します。

HTML
<div id="parent">
  <span id="child">子要素</span>
</div>
mouseover(子要素で余計に発火する)
const parent = document.getElementById("parent");

parent.addEventListener("mouseover", () => {
  console.log("mouseover"); // 親に入ったとき + 子要素に移動したときにも発火
});
parent.addEventListener("mouseout", () => {
  console.log("mouseout");  // 子要素に移動したときにも発火
});
mouseenter(要素自身のみ発火)
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 画面(モニター)の左上 マルチモニター判定(ほぼ使わない)
JavaScript
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 で追従
CSS だけで十分な例
.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);
}
スタイル変更だけなら CSS :hover が最も効率的です。JavaScript は DOM 操作・データ取得・座標追従など、CSS では実現できない処理に使いましょう。

Pointer Events でタッチ・ペンにも対応する

マウスイベントはマウス専用ですが、Pointer Events はマウス・タッチ・ペンを統一的に扱えるAPI です。

Mouse Event Pointer Event 対応するデバイス
mouseenter pointerenter マウス + タッチ + ペン
mouseleave pointerleave マウス + タッチ + ペン
mousemove pointermove マウス + タッチ + ペン
mousedown pointerdown マウス + タッチ + ペン
JavaScript
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 以降のすべてのモダンブラウザで対応しています。

実務でよく使うパターン

ツールチップ(マウス座標に追従)

JavaScript
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";
  });
});

ドロップダウンメニュー(遅延付き)

JavaScript
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);
});
ドロップダウンの開閉に遅延を入れることで、マウスが一瞬外れただけでメニューが閉じてしまう問題を防げます。

画像のホバープレビュー

JavaScript
document.querySelectorAll(".thumbnail").forEach(thumb => {
  thumb.addEventListener("mouseenter", () => {
    const mainImg = document.getElementById("mainImage");
    mainImg.src = thumb.getAttribute("data-full-src");
    mainImg.alt = thumb.alt;
  });
});

マウス追従エフェクト(カーソルカスタマイズ)

CSS
.cursor-dot {
  position: fixed;
  width: 12px;
  height: 12px;
  background: #0284c7;
  border-radius: 50%;
  pointer-events: none;
  transition: transform 0.1s ease;
  z-index: 9999;
}
JavaScript
const dot = document.querySelector(".cursor-dot");

document.addEventListener("mousemove", (e) => {
  dot.style.left = e.clientX + "px";
  dot.style.top = e.clientY + "px";
});

関連記事

よくある質問

Qmouseenter と mouseover はどちらを使うべきですか?
Aほとんどの場面では mouseenter / mouseleave を使ってください。mouseover / mouseout はバブリングするため、子要素への移動で余計に発火します。イベント委任(親要素でまとめて処理)が必要な場合のみ mouseover を使います。
QCSS の :hover と JavaScript のマウスイベントはどう使い分けますか?
A背景色やサイズの変更などスタイル変更だけなら CSS :hover が最適です。JavaScript は DOM 操作(テキスト差し替え)、データ取得(API)、座標追従(ツールチップ)など CSS では実現できない処理に使います。
Qスマホでマウスイベントは動作しますか?
Aタッチデバイスではマウスイベントは原則動作しません。マウスとタッチの両方に対応するには Pointer Events(pointerenter / pointerleave 等)を使うか、touchstart / mouseenter の両方を登録してください。
Qmousemove のパフォーマンスが心配です。
Amousemove は毎秒数十回発火します。DOM 操作や計算を行う場合は throttlerequestAnimationFrame で実行回数を制限してください。座標の表示やカーソルドットの移動程度であれば、通常パフォーマンス問題は発生しません。
Qドロップダウンメニューがマウスを少し動かしただけで閉じてしまいます。
Amouseleave で即座に閉じるのではなく、setTimeout で 200〜300ms の遅延を入れてください。マウスがメニューに戻った場合は clearTimeout で閉じる処理をキャンセルします。

まとめ

JavaScript のマウスイベントの使い方を整理しました。

  • ホバー検出: mouseenter / mouseleave(バブリングしない、推奨)
  • 座標取得: mousemoveclientX / clientY(ビューポート基準)
  • CSS との使い分け: スタイル変更は CSS :hover、DOM 操作・座標追従は JavaScript
  • タッチ対応: Pointer Events(pointerenter 等)で統一
  • パフォーマンス: mousemove には throttle / rAF を適用

ツールチップやドロップダウンメニューなど、マウス操作に応じた UI はユーザー体験を大きく向上させます。CSS :hover で済む場面と JavaScript が必要な場面を使い分けて、効率的に実装しましょう。