jQueryを使ったタブ切り替えは、商品説明・FAQ・プロフィールページなどでよく使われるUIです。
この記事では、シンプルな実装からフェード・スライドアニメーション・ハッシュURL対応まで、実務でそのまま使える4パターンをプレビュー+コード付きで解説します。
この記事でわかること
- シンプルなタブ切り替え(data属性でパネルを紐付け)
- フェードアニメーション付きタブ(opacity + transition)
- アンダーラインがスライドするタブ(position() で位置を取得)
- ハッシュURLで直リンク可能なタブ(history.pushState)
前提:jQueryの読み込み
この記事のサンプルはすべて jQuery 3.x を前提としています。HTMLの <head> か </body> 直前に以下を追加してください。
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
シンプルなタブ切り替え
ボタンに data-target 属性でパネルのIDを指定し、クリック時に active クラスを付け替えるシンプルな実装。同一ページに複数設置しても独立して動作する。
タブ 2
タブ 3
タブ 1 のコンテンツ。クリックで切り替わります。
タブ 2 のコンテンツ。アニメーションなしのシンプルな切り替え。
タブ 3 のコンテンツ。追加・削除がしやすいシンプル構成。
<div class="tab-wrap"> <!-- タブボタン --> <div class="tab-list"> <button class="tab-btn active" data-target="panel-1">タブ 1</button> <button class="tab-btn" data-target="panel-2">タブ 2</button> <button class="tab-btn" data-target="panel-3">タブ 3</button> </div> <!-- タブパネル --> <div class="tab-panel active" id="panel-1"> <p>タブ 1 のコンテンツ</p> </div> <div class="tab-panel" id="panel-2"> <p>タブ 2 のコンテンツ</p> </div> <div class="tab-panel" id="panel-3"> <p>タブ 3 のコンテンツ</p> </div> </div>
/* ── タブボタン ── */ .tab-list { display: flex; gap: 4px; border-bottom: 2px solid #e4e2dd; margin-bottom: 0; } .tab-btn { padding: 10px 20px; background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -2px; /* border-bottom と重ねて親のボーダーを上書き */ cursor: pointer; font-size: 14px; color: #6b6b6b; transition: color .2s, border-color .2s; } .tab-btn.active { color: #3e8685; border-bottom-color: #3e8685; font-weight: 600; } /* ── タブパネル ── */ .tab-panel { display: none; /* デフォルトは非表示 */ padding: 20px; background: #fff; border: 1px solid #e4e2dd; border-top: none; border-radius: 0 0 6px 6px; } .tab-panel.active { display: block; /* active クラスがついたパネルだけ表示 */ }
$(function () { /* タブボタンをクリックしたとき */ $('.tab-btn').on('click', function () { var target = $(this).data('target'); /* 同じ .tab-wrap 内のボタン・パネルだけを操作する */ var $wrap = $(this).closest('.tab-wrap'); /* すべてのボタンとパネルから active を外す */ $wrap.find('.tab-btn').removeClass('active'); $wrap.find('.tab-panel').removeClass('active'); /* クリックしたボタンと対応するパネルに active を付ける */ $(this).addClass('active'); $('#' + target).addClass('active'); }); });
フェードアニメーション付きタブ
position: absolute で全パネルを重ね、opacity の transition でフェードする実装。display: none では CSS アニメーションが効かないため、opacity + pointer-events の組み合わせを使う。
仕様
よくある質問
フェードアニメーション付きのタブです。切り替え時に opacity と transition でなめらかに切り替わります。
仕様タブのコンテンツ。display: none ではなく opacity: 0 / 1 + position で実装するとフェードが効きます。
よくある質問のコンテンツ。商品ページ・ランディングページのタブによく使われる形式です。
<div class="tab-wrap tab-fade"> <div class="tab-list"> <button class="tab-btn active" data-target="panel-1">概要</button> <button class="tab-btn" data-target="panel-2">仕様</button> <button class="tab-btn" data-target="panel-3">よくある質問</button> </div> <!-- パネルを position: relative のラッパーで囲む --> <div class="tab-panels-wrap"> <div class="tab-panel active" id="panel-1">概要のコンテンツ</div> <div class="tab-panel" id="panel-2">仕様のコンテンツ</div> <div class="tab-panel" id="panel-3">よくある質問のコンテンツ</div> </div> </div>
/* ── フェード付きタブ ── */ .tab-panels-wrap { position: relative; min-height: 120px; } .tab-fade .tab-panel { position: absolute; /* 全パネルを重ねて配置する */ top: 0; left: 0; right: 0; opacity: 0; /* デフォルトは透明 */ pointer-events: none; /* 非表示中はクリックを貫通させる */ transition: opacity .3s ease; padding: 20px; background: #fff; } .tab-fade .tab-panel.active { position: relative; /* active は通常フローに戻してラッパーの高さを決める */ opacity: 1; pointer-events: auto; }
$(function () { /* フェード付きタブ(.tab-fade クラスを持つ場合) */ $('.tab-fade .tab-btn').on('click', function () { var target = $(this).data('target'); var $wrap = $(this).closest('.tab-wrap'); /* ボタンの active を付け替える */ $wrap.find('.tab-btn').removeClass('active'); $(this).addClass('active'); /* フェードアウト → フェードイン */ $wrap.find('.tab-panel.active').removeClass('active'); $('#' + target).addClass('active'); }); });
アンダーラインがスライドするタブ
タブ切り替え時に下線が left と width のアニメーションでスライドするデザイン。position() でボタンの位置を取得し、$.css() で動的に下線を移動させる。
Web制作
SEO
WordPress
すべてのコンテンツを表示します。フィルタリングタブにも応用できます。
Web制作カテゴリのコンテンツ。
SEOカテゴリのコンテンツ。
WordPressカテゴリのコンテンツ。
<div class="tab-wrap"> <!-- アンダーラインバーを含むタブリスト --> <div class="tab-list tab-underline"> <button class="tab-btn active" data-target="panel-all">すべて</button> <button class="tab-btn" data-target="panel-web">Web制作</button> <button class="tab-btn" data-target="panel-seo">SEO</button> <!-- アンダーラインバー(JSで位置・幅を動かす) --> <div class="tab-underline-bar"></div> </div> <div class="tab-panel active" id="panel-all">すべて</div> <div class="tab-panel" id="panel-web">Web制作</div> <div class="tab-panel" id="panel-seo">SEO</div> </div>
.tab-underline { position: relative; border-bottom: 2px solid #e4e2dd; } .tab-underline .tab-btn { border-bottom: none; /* 個別の下線は非表示 */ color: #6b6b6b; } .tab-underline .tab-btn.active { color: #3e8685; font-weight: 600; } /* スライドするアンダーライン */ .tab-underline-bar { position: absolute; bottom: -2px; /* 親の下ボーダーに重ねる */ height: 2px; background: #3e8685; transition: left .25s ease, width .25s ease; /* 位置と幅をなめらかに動かす */ }
$(function () { /* アンダーバーを active ボタンの位置・幅に合わせる */ function moveBar($btn) { var $bar = $btn.closest('.tab-underline').find('.tab-underline-bar'); $bar.css({ left: $btn.position().left, /* ボタンの左端位置 */ width: $btn.outerWidth() /* ボタンの幅 */ }); } /* 初期位置を設定 */ moveBar($('.tab-underline .tab-btn.active')); $('.tab-underline .tab-btn').on('click', function () { var target = $(this).data('target'); var $wrap = $(this).closest('.tab-wrap'); $wrap.find('.tab-btn').removeClass('active'); $wrap.find('.tab-panel').removeClass('active'); $(this).addClass('active'); $('#' + target).addClass('active'); moveBar($(this)); }); });
ハッシュURLで直リンク可能なタブ
タブ切り替え時に history.pushState() でURLのハッシュを更新する。SNSシェアや外部から特定タブへ直接リンクが可能になる。ページ読み込み時に location.hash を確認してタブを自動で開く。
実績
お問い合わせ
ハッシュURLに対応したタブです。URLの末尾が #tab-profile の形式になり、直接リンクが可能になります。
URLが #tab-works に変わります。ブラウザの戻るボタンでも前のタブに戻れます。
URLが #tab-contact に変わります。SNSで特定タブへの直リンが可能になります。
<!-- data-hash 属性にURLのハッシュ値を設定 --> <div class="tab-wrap"> <div class="tab-list"> <button class="tab-btn active" data-target="panel-profile" data-hash="tab-profile">プロフィール</button> <button class="tab-btn" data-target="panel-works" data-hash="tab-works">実績</button> <button class="tab-btn" data-target="panel-contact" data-hash="tab-contact">お問い合わせ</button> </div> <div class="tab-panel active" id="panel-profile">プロフィール内容</div> <div class="tab-panel" id="panel-works">実績内容</div> <div class="tab-panel" id="panel-contact">お問い合わせ内容</div> </div>
/* 基本スタイルはシンプルタブ(01)と同じ */ /* 追加CSSは不要 */
$(function () { /* ハッシュ値でタブを開く共通関数 */ function openTabByHash(hash) { $('.tab-btn[data-hash="' + hash + '"]').trigger('click'); } /* ページ読み込み時にURLのハッシュを確認して該当タブを開く */ if (location.hash) { var hash = location.hash.replace('#', ''); openTabByHash(hash); } $('.tab-btn').on('click', function () { var target = $(this).data('target'); var hash = $(this).data('hash'); var $wrap = $(this).closest('.tab-wrap'); $wrap.find('.tab-btn').removeClass('active'); $wrap.find('.tab-panel').removeClass('active'); $(this).addClass('active'); $('#' + target).addClass('active'); /* URLにハッシュを付与(ページスクロールなし・履歴には残す) */ if (hash) history.pushState(null, null, '#' + hash); }); });
まとめ
- 基本構造は「ボタンの
data-target= パネルのid」で紐付け .closest()で同じ.tab-wrap内だけ操作すれば複数設置に対応できる- フェードには
display: noneではなくopacity + pointer-eventsを使う - スライドアンダーラインは
.position()でボタン位置を取得して動かす - 直リンク対応は
history.pushState()とlocation.hashの組み合わせ