【jQueryの基本】タブコンテンツ切り替え

この記事は最終更新日から 1 年以上が経過しており、内容が古くなっている可能性があります。

基本のタブ切り替え

<div class="js-tab-container tab-container">
  <ul class="tab-menu flex text-center">
    <li class="js-tab-menu__item tab-menu__item is-active">タブ1</li>
    <li class="js-tab-menu__item tab-menu__item">タブ2</li>
    <li class="js-tab-menu__item tab-menu__item">タブ3</li>
  </ul>
  <div class="tab-content">
    <div class="js-tab-content__item  tab-content__item is-active">
      コンテンツ1
    </div>
    <div class="js-tab-content__item tab-content__item">コンテンツ2</div>
    <div class="js-tab-content__item tab-content__item">コンテンツ3</div>
  </div>
</div>

アクティブなタブと表示コンテンツにis-activeclass を付与しておく。

/* 一部抜粋 */
.tab-content__item {
  opacity: 0;
  height: 0;
  overflow: hidden;
}
.tab-content__item.is-active {
  opacity: 1;
  transition: opacity 0.4s ease-in-out;
  overflow: visible;
  height: auto;
  padding: 20px;
  border: 2px solid #aaa;
}

コンテンツの表示・非表示はdisplayプロパティが一般的だが、タブ切り替えでふわっと表示させる効果を付けるためopacityプロパティを変更することで実装した。

  • 非表示コンテンツ:透明度0、高さ0、コンテンツからはみ出す領域を隠す(overflow: hidden
  • 表示コンテンツ(is-activeclass):透明度1、高さauto、はみ出す領域を表示(overflow: visible
  • paddingプロパティはis-activeの状態の時のみ設定する(display:blockと違って、overflow: hiddenではpadding領域が隠れない)。
$(function () {
  $(".js-tab-menu__item").on("click", function () {
    const index = $(".js-tab-menu__item").index(this);
    $(".js-tab-menu__item, .js-tab-content__item").removeClass("is-active");
    $(this).addClass("is-active");
    $(".js-tab-content__item").eq(index).addClass("is-active");
  });
});
  • タブ.js-tab-menu__itemをクリックしたときに、クリックしたタブ(this)の順番(index)を取得
  • 全てのタブとタブコンテンツからis-activeclass を削除
  • クリックしたタブとそのタブに対応するタブコンテンツにis-activeclass を付与する

これでひとまず、タブ切り替えが実装された。

しかしこのコードでは、複数設置した場合に挙動がおかしくなる。以下はその例である。1 個目のタブコンテナ内のタブをクリックすると、2 個目のタブコンテナ内コンテンツも反応して非表示になる。js-tab-content__itemclass が付与されたすべてのタブコンテンツが連動してしまうからだ。

複数タブに対応したタブ切り替え

$(function () {
  $(".js-tab-menu__item").on("click", function () {
    const tabGroup = $(this).parents(".js-tab-group");
    const tabMenu = tabGroup.find(".js-tab-menu__item");
    const tabContent = tabGroup.find(".js-tab-content__item");
    tabMenu.removeClass("is-active");
    $(this).addClass("is-active");
    const index = tabMenu.index(this);
    tabContent.removeClass("is-active");
    tabContent.eq(index).addClass("is-active");
  });
});
  • tabGroup:クリックしたタブおよびコンテンツの全体を囲む祖先要素js-tab-group
  • tabMenu:祖先要素内のタブjs-tab-menu__item
  • tabContent:祖先要素内のコンテンツjs-tab-content__item

祖先要素に遡って指定することでタブ切り替えの挙動が同一コンテナ内に制限するができ、複数タブコンテナへの対応が可能となる。

タブコンテンツ内からタブ切り替え(複数タブ対応)

需要がどれだけあるかは不明だが、タブコンテンツ内のアンカーをクリックして、次のタブを選択・対象のコンテンツを表示することもできる。

$(function() {
	...(略)
	// 追加
	$('.js-anchor').on('click', function() {
		const tabGroup = $(this).parents('.js-tab-group');
		const tabContent = $(this).parents('.js-tab-content__item');
		const index = tabContent.index();
		const tabMenu = tabGroup.find('.js-tab-menu__item');
		const position = $('.js-tab-group').offset().top;
		tabMenu.removeClass('is-active');
		tabMenu.eq(index).next().addClass('is-active');
		tabContent.removeClass('is-active').next().addClass('is-active');
		$('html, body').animate({scrollTop: position - 20 }, 500, "swing");
	});
});

コンテンツ内アンカーの class をjs-anchorとする。

  • 変数tabContent:アンカーの親であるコンテンツ
  • 変数index:親コンテンツの順番を取得
  • すべてのタブとコンテンツからis-activeclass を削除
  • 親タブの次のタブとそれに対応するコンテンツにis-activeclass を付与

スマホ版の場合はコンテンツが長くなるので、アンカークリックで次のタブを選択したときに、タブコンテンツ上部位置までスクロールするようにした。

  • 変数positionjs-tab-groupの位置を取得(offset().top
  • scrollTopで対象の位置までスクロールする(js-tab-groupより 20px 上の位置になるよう調整)

varとは異なり、constはスコープ内でのみ有効な変数なので、先ほどのスクリプトと変数名が同じでも問題はない(が、紛らわしいと感じる場合は別の変数名にした方がいいかもしれない)。

Suzunatsu

このブログについて

コーディングやWeb関連技術の記事と、買い物など日々のメモから成り立っています。 →少しだけ詳しく

この記事がお役に立ちましたら、サポートをお願いします。

広告