【jQuery】マウスオーバーで画像を切り替える完全ガイド|hover・data属性・フェード・touch対応まで

jQueryの hover() を使うと、マウスオーバー時に画像を別の画像へ切り替えられます。商品のカラーバリエーション・バナーのハイライト・ギャラリーのプレビューなど、UXを高める画像インタラクションを短いコードで実現できます。

この記事でわかること

  • hover()でsrc属性を変更する基本パターン
  • data-*属性でhover先画像を管理する(保守性向上)
  • フェード効果付きのクロスフェード画像切り替え
  • 複数画像をまとめてeach()で登録する
  • タッチデバイス(スマートフォン)への対応方法
  • CSSのみの実装との比較・使い分け
スポンサーリンク

hover()でsrc属性を変更する基本実装

jQueryの hover() は、マウスオーバー時と離れたときの2つの関数を受け取ります。コールバック内で attr("src", ...) を使ってsrc属性を書き換えるのが最もシンプルな実装です。

HTMLと基本のjQuery

<img id="product-img" src="product-default.jpg" alt="商品画像" width="300" height="200">
$(function () {
  $('#product-img').hover(
    function () {
      // マウスオーバー: 画像を切り替える
      $(this).attr('src', 'product-hover.jpg');
    },
    function () {
      // マウスアウト: 元の画像に戻す
      $(this).attr('src', 'product-default.jpg');
    }
  );
});
$(this)はhoverした要素を指す
hover()コールバック内の this はイベント対象の要素(imgタグ)です。$(this).attr("src", ...) で対象要素だけを操作できます。attr()の詳細はattr()完全ガイドを参照してください。

data-*属性でhover先画像を管理する

HTMLに data-hover 属性として切り替え先画像を書いておくと、jQueryのコードを変更せずにHTMLだけで画像を管理できます。複数の画像を扱う場合に特に有効です。

<!-- data-hover属性に切り替え先画像のパスを書く -->
<img class="hover-img" src="item1-default.jpg" data-hover="item1-hover.jpg" alt="商品1" width="200">
<img class="hover-img" src="item2-default.jpg" data-hover="item2-hover.jpg" alt="商品2" width="200">
<img class="hover-img" src="item3-default.jpg" data-hover="item3-hover.jpg" alt="商品3" width="200">
$(function () {
  $('.hover-img').hover(
    function () {
      var $img = $(this);
      // data-hover属性から切り替え先を取得
      $img.data('original', $img.attr('src'))  // 元画像を保存
          .attr('src', $img.data('hover'));
    },
    function () {
      // 保存した元画像に戻す
      $(this).attr('src', $(this).data('original'));
    }
  );
});
data()とattr(“data-*”)の違い

  • $(el).data("hover"): jQuery内部のキャッシュから読む。HTML属性の変更を後から反映しない
  • $(el).attr("data-hover"): その都度DOM属性を読む。常に最新の値を取得
  • HTMLで最初から決まっている値を読む場合はどちらでも同じ結果になります

data属性の詳細はdata属性の取得・設定・入れ子完全ガイドを参照してください。

フェード効果付きの画像切り替え(クロスフェード)

瞬時に切り替わるのではなく、フェードイン/フェードアウトでなめらかに画像を切り替えるパターンです。CSSで position:absolute を重ねてクロスフェードを実現します。

CSSの設定

.img-wrapper {
  position: relative;
  display: inline-block;
  width: 300px;
  height: 200px;
}
.img-wrapper img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.img-wrapper .img-hover {
  opacity: 0;  /* 最初は非表示 */
}

HTMLとjQuery

<div class="img-wrapper">
  <img class="img-default" src="product-default.jpg" alt="通常時">
  <img class="img-hover"   src="product-hover.jpg"   alt="ホバー時">
</div>
$(function () {
  $('.img-wrapper').hover(
    function () {
      // ホバー画像をフェードイン
      $(this).find('.img-hover').stop(true).fadeTo(300, 1);
    },
    function () {
      // ホバー画像をフェードアウト(元画像が見える)
      $(this).find('.img-hover').stop(true).fadeTo(300, 0);
    }
  );
});
stop(true)で前のアニメーションをキャンセル
マウスを素早く出し入れするとアニメーションが積み重なる問題を防ぐため、stop(true) で前のアニメーションをクリアしてから新しいアニメーションを開始します。アニメーションの詳細はアニメーション完全ガイドを参照してください。

複数画像をeach()でまとめて登録する

商品一覧など同じクラスの画像が複数ある場合、each() を使わずに共通クラスにhoverを登録するだけで全て処理できます。また、hover前に画像を事前読み込みしておくことで切り替え時のちらつきを防げます。

<div class="product-list">
  <img class="product-thumb" src="p1.jpg" data-hover="p1-hover.jpg" alt="商品A">
  <img class="product-thumb" src="p2.jpg" data-hover="p2-hover.jpg" alt="商品B">
  <img class="product-thumb" src="p3.jpg" data-hover="p3-hover.jpg" alt="商品C">
</div>
$(function () {
  // ホバー画像を事前読み込み(ちらつき防止)
  $('.product-thumb').each(function () {
    var hoverSrc = $(this).data('hover');
    if (hoverSrc) {
      var img = new Image();
      img.src = hoverSrc;  // ブラウザキャッシュに乗せる
    }
  });

  // まとめてhover登録
  $('.product-thumb').hover(
    function () {
      $(this).attr('src', $(this).data('hover'));
    },
    function () {
      $(this).attr('src', $(this).attr('src').replace('-hover', ''));
    }
  );
});
src文字列の操作は命名規則を統一する
replace("-hover", "") で元画像名を復元する方法は、ファイル名に規則性がある場合のみ有効です。命名規則がまちまちな場合は、先のサンプルのように data("original") に元画像を保存しておく方法が確実です。

タッチデバイス対応(スマートフォン)

スマートフォンではマウスオーバーイベントが発生しません。タッチデバイスでも画像切り替えを動作させるには touchstart/touchend を追加します。

$(function () {
  var $img = $('#product-img');
  var defaultSrc = $img.attr('src');
  var hoverSrc   = $img.data('hover');

  // マウス操作(PC)
  $img.on('mouseenter', function () {
    $(this).attr('src', hoverSrc);
  }).on('mouseleave', function () {
    $(this).attr('src', defaultSrc);
  });

  // タッチ操作(スマートフォン・タブレット)
  $img.on('touchstart', function () {
    $(this).attr('src', hoverSrc);
  }).on('touchend touchcancel', function () {
    $(this).attr('src', defaultSrc);
  });
});
スマートフォンではタップ時のみ切り替わる
PCではマウスを乗せている間は切り替わったままですが、スマートフォンでは touchend が発生するとすぐに元に戻ります。「タップで切り替えを固定したい」場合は touchend でのリセットをやめ、別のタップや他の操作でリセットするロジックを追加してください。デバイス判定の詳細はデバイス判定完全ガイドを参照してください。

CSSのみ(:hover擬似クラス)との比較・使い分け

画像の切り替えはCSSだけでも実装できます。jQueryを使う場合とCSSのみを使う場合の特徴を比較します。

方法 コード量 パフォーマンス 動的制御 向いている用途
jQuery hover() 少なめ 普通 ◎(条件分岐・API連携可) クリック後の切り替え固定・動的な画像URL変更
CSSのみ(:hover) HTMLとCSSのみ 高い(GPU) △(静的のみ) 単純な装飾・アイコンのロールオーバー
CSSトランジション+jQuery 中程度 高い フェードなどのアニメーション付き切り替え

CSSのみで実装する例(background-imageを切り替える)

<div class="hover-banner"></div>
.hover-banner {
  width: 300px;
  height: 200px;
  background-image: url("banner-default.jpg");
  background-size: cover;
  transition: background-image 0s;  /* 瞬時に切り替わる */
}
.hover-banner:hover {
  background-image: url("banner-hover.jpg");
}
background-imageはトランジションが効かない
CSSの transitionbackground-image には適用されません。フェードなどのアニメーション付き切り替えはjQueryの fadeTo() やCSSの opacity を使った重ね表示を組み合わせる必要があります。

まとめ

jQueryでマウスオーバー時に画像を切り替えるには、hover() コールバック内で attr("src", ...) を変更するのが最もシンプルです。data-hover 属性で切り替え先を管理することでHTMLとJSを分離できます。フェード効果が必要な場合はCSSで重ね表示 + fadeTo()、スマートフォン対応が必要な場合は touchstart/touchend を追加してください。

関連記事: attr()完全ガイド / アニメーション完全ガイド / デバイス判定完全ガイド / data属性の取得・設定完全ガイド

よくある質問(FAQ)

Q画像が切り替わる際に一瞬ちらつきます。
Aホバー先の画像がブラウザにキャッシュされていないため、初回のみ読み込みに時間がかかっています。ページ読み込み時に new Image(); img.src = hoverSrc; で画像を事前読み込みしてください。2回目以降はキャッシュから読まれるためちらつきがなくなります。
Qマウスを素早く出し入れするとアニメーションが遅れて追いつきません。
AjQueryのアニメーションキューに処理が積み重なっている状態です。フェードアニメーションを使っている場合は $(this).stop(true).fadeTo(300, 1) のようにstop(true) でキューをクリアしてから新しいアニメーションを開始してください。
Qスマートフォンでhoverが動きません。
Aスマートフォンにはmouseenterイベントが発生しません。touchstart/touchend イベントを追加してください。または、CSS :hover を使う場合はiOSのSafariでは最初のタップでhoverが発動し、別の場所をタップすると解除される動作になります。
Qhover()ではなくon()でmouseenterを使う理由はありますか?
Ahover(fn1, fn2)on("mouseenter", fn1).on("mouseleave", fn2) のショートカットです。動的に追加した要素にホバーを適用する場合は、イベント委譲 $(document).on("mouseenter", ".hover-img", fn) を使ってください。hover() は委譲形式に対応していないため、動的要素には直接使えません。
Qimgタグ以外(div・ボタン)でも同じ方法で画像を切り替えられますか?
Adivやボタンの場合は src 属性がないため、内包する img タグを find("img") で取得してsrcを変更するか、CSSの background-image$(this).css("background-image", "url(...)") で変更してください。