JavaScript

This page shows how to use the JavaScript onMouseOver event (mouseover) to run code the moment the mouse pointer enters an element, and how to avoid common pitfalls such as repeated firing inside child elements.

onmouseover

マウスカーソルが要素の上に「乗った瞬間」に、ちょっとした反応(色を変える、メッセージを出す、プレビューを切り替えるなど)を JavaScript で付けたいときに使う onmouseovermouseover)の使いどころと基本パターンをまとめたページ。

onmouseover とは?

onmouseover(正式には mouseover イベント/onmouseover プロパティ)は、マウスカーソルが要素の上に入った瞬間に実行されるイベントハンドラです。

「ボタンに乗せたらハイライトする」「カードに触れたら詳細を出す」「サムネに触れたらプレビューを切り替える」など、いわゆる “ホバー(hover)” の動きを JavaScript 側で制御したいときに出番があります。

ただし、見た目の変化だけで済む場合は、まず CSS の :hover を検討するのが基本です。JavaScript が必要なのは「状態を持つ」「条件分岐する」「ログや通信をしたい」など、見た目だけでは完結しないときです。

以下は、onmouseover の基本的な使い方です。

HTML

<div id="hoverBox">ここにマウスを乗せてください</div>

JavaScript

// 要素の onmouseover プロパティにハンドラーを追加する
document.getElementById("hoverBox").onmouseover = function () {
    console.log("mouseover: 乗りました");
};

Sample

ここにマウスを乗せてください

この例では、div 要素に対して onmouseover を設定し、カーソルが入ったタイミングでメッセージを表示しています。

なお、onmouseover は “プロパティ” なので、同じ要素に別の onmouseover を代入すると上書きされます。複数の処理を安全に追加したい場合は、次の addEventListener を使うのが一般的です。

実務では addEventListener が基本になりやすい

onmouseover でも動きますが、実務ではだいたい次の理由で addEventListener の方が扱いやすいです。

JavaScript

const box = document.getElementById("hoverBox");

box.addEventListener("mouseover", function () {
    console.log("addEventListener: mouseover");
});

HTML に onmouseover="..." と直接書く方法もありますが、HTML と JavaScript が混ざりやすく保守しづらいので、基本は「JS側でまとめて登録」でOKです。

onmouseover が何度も発火する理由(ここが一番つまずきやすい)

mouseover(= onmouseover)は、要素の中に子要素があると「親に入った」「子に入った」「子から別の子に移動した」などでも発火しやすい、という特徴があります。

つまり、親要素に対して mouseover を付けると、子要素の上にマウスが移動しただけで “何回も” 起きることがあります。バグっぽく見えますが、仕様としてよくある動きです。

HTML

<div id="card" style="padding: 16px; border: 1px solid #ccc;">
    親要素
    <button style="margin-left: 8px;">子ボタン</button>
</div>

<p id="cardLog"></p>

JavaScript

const card = document.getElementById("card");
const cardLog = document.getElementById("cardLog");
let count = 0;

card.addEventListener("mouseover", function () {
    count += 1;
    cardLog.textContent = "mouseover 発火: " + count + " 回";
});

Sample

親要素

「カード全体に入ったときに 1 回だけ処理したい」のに回数が増える場合、次の mouseenter の方が素直に作れます。

mouseenter と使い分ける(1回だけ反応したいなら)

mouseenter は、その要素に入った瞬間を扱いやすいイベントです。mouseover と違って、子要素への移動で増えにくく、バブリングもしません。

JavaScript

const card = document.getElementById("card");

card.addEventListener("mouseenter", function () {
    card.style.background = "#f5f5f5";
});

card.addEventListener("mouseleave", function () {
    card.style.background = "";
});

「入ったとき」「出たとき」をセットで作るなら、mouseenter + mouseleave は相性が良いです。

ただし、後で紹介するイベント委譲(親にまとめて付ける)をしたい場合は、バブリングする mouseover の方が都合がいい場面もあります。

relatedTarget を使って “外から入った時だけ” に絞る

mouseover / mouseout には「どこから来た/どこへ行った」が入っています。これが event.relatedTarget です。

親要素の中で子要素に移動しただけなら無視して、外から入ったときだけ反応させたい場合は、relatedTargetcontains() を組み合わせると、暴発を抑えられます。

JavaScript

const card = document.getElementById("card");

card.addEventListener("mouseover", function (event) {
    const from = event.relatedTarget;

    // カードの中からカードの中へ移動しただけなら無視する
    if (from && card.contains(from)) {
        return;
    }

    card.style.outline = "2px solid #666";
});

card.addEventListener("mouseout", function (event) {
    const to = event.relatedTarget;

    // カードの中からカードの中へ移動しただけなら無視する
    if (to && card.contains(to)) {
        return;
    }

    card.style.outline = "";
});

この考え方を知っていると、「mouseover が増えるのをバグ扱いせず、仕様として制御する」方向に進めます。

イベント委譲(たくさんの要素をまとめて扱う)

サムネ画像が増える・リスト項目が動的に増える、などのケースでは、各要素にリスナーを付けるより、親要素に 1 個だけ付けてまとめるほうが管理がラクです。これがイベント委譲です。

このとき、バブリングする mouseover は相性が良いです。

HTML

<img id="mainPreview" src="main.jpg" alt="メイン画像" style="width: 320px; display: block; margin-bottom: 8px;">

<div id="thumbs">
    <img src="a.jpg" alt="サムネA" style="width: 80px; cursor: pointer;">
    <img src="b.jpg" alt="サムネB" style="width: 80px; cursor: pointer;">
    <img src="c.jpg" alt="サムネC" style="width: 80px; cursor: pointer;">
</div>

JavaScript

const main = document.getElementById("mainPreview");
const thumbs = document.getElementById("thumbs");

thumbs.addEventListener("mouseover", function (event) {
    const img = event.target.closest("img");
    if (!img) {
        return;
    }

    main.src = img.src;
});

event.target は “いちばん内側” になりがちなので、closest() で目的の要素を探すと安定します。親側でまとめて扱えるので、要素が増減しても壊れにくいです。

パフォーマンスと運用の注意点(中級者以上向け)

たとえばツールチップのような UI は、hover だけにするとスマホやキーボード操作で詰みやすいので、focus でも出るようにしておくと安心です。

よくある質問

CSS の :hover と onmouseover はどっちを使うべき?
見た目が変わるだけなら :hover が基本おすすめです。状態管理・ログ・分岐など、JavaScript の処理が必要なら onmouseover(または addEventListener("mouseover", ...))を使います。
onmouseover が何回も動くのはバグ?
子要素に入っただけでも発火する仕様なので、よく起きます。1回だけにしたいなら mouseenter を検討するか、relatedTarget を使って「外から入った時だけ」に絞ります。
onmouseover と mouseenter の違いは?
mouseover はバブリングして子要素移動でも発火しやすい一方、mouseenter は基本的にその要素に入った瞬間だけで、バブリングしません。
スマホでも onmouseover は使える?
タッチ環境では hover が前提どおりに動かないことがあります。重要な操作はクリック(タップ)やフォーカスでも同じ結果になるように作るのが安全です。
HTML に onmouseover="..." と書くのはアリ?
学習としてはアリですが、実務では addEventListener で分けて書くほうが保守しやすいことが多いです。

よくあるエラー早見表

イベントが発火しない
要素の取得(getElementById の ID)が間違っている、またはスクリプトが先に実行されて要素がまだ存在しない可能性があります。HTMLの後ろで読み込むか、deferDOMContentLoaded を使います。
何回も発火して暴走して見える
mouseover の仕様で、子要素への移動でも発火します。mouseenter に切り替えるか、relatedTarget を使って「外から入った時だけ」に絞ります。
思った要素が取れない(target がズレる)
event.target は内側の要素になりがちです。親にまとめたい場合は event.currentTarget を見るか、closest() で目的の要素を探します。
hover しただけで重くなる
mouseover で重い DOM 操作や通信をすると体感が悪くなります。見た目だけなら CSS に寄せる、処理を間引く(デバウンス)などが有効です。
スマホで反応しない/挙動が変
タッチ環境は hover 前提が崩れます。クリック(タップ)でも同じ結果になる設計にすると安全です。