タブ切り替えコンテンツの中にslickスライダーを入れたら、最初のタブでは正常なのに、2つ目以降のタブを開くとスライダーが表示されない・画像が横並びにならない——slickとタブを組み合わせたときに起きやすい、定番のトラブルです。
結論から言うと、原因は非表示(display: none)の状態でslickが初期化され、幅や高さを正しく計算できないことです。解決策は、タブが切り替わったタイミングでsetPositionを呼び、レイアウトを再計算させることです。この記事では、問題の現象と原因、そして具体的な解決方法を解説します。
まず、前提となるコードを確認します。タブのパネルそれぞれにスライダーを設置した構成です。
<div class="tab_box"> <div class="btn_area"> <div class="tab_btn active">タブ1</div> <div class="tab_btn">タブ2</div> </div> <div class="panel_area"> <!-- タブコンテンツ1 --> <div class="tab_panel active"> <div class="slider"> <div><img src="image1.jpg" /></div> <div><img src="image2.jpg" /></div> <div><img src="image3.jpg" /></div> </div> </div> <!-- タブコンテンツ2 --> <div class="tab_panel"> <div class="slider"> <div><img src="image1.jpg" /></div> <div><img src="image2.jpg" /></div> <div><img src="image3.jpg" /></div> </div> </div> </div> </div>
非アクティブなタブパネルは、CSSでdisplay: noneにして隠しています。
.tab_box .tab_panel {
display: none;
}
.tab_box .tab_panel.active {
display: block;
}
そして、スライダーをまとめて初期化します。
$(".slider").slick({});
この状態だと、最初に表示されているタブ1内のスライダーは問題なく動きますが、タブ2を開いたときにスライダーが表示されなかったり、崩れて表示されたりします。
原因は、slickが初期化されるタイミングにあります。slickは、スライダーの要素が表示されている状態で初期化されることを前提に設計されています。表示されている要素なら、幅や高さを正しく計測できるからです。
ところが、タブ2以降のパネルはdisplay: noneで非表示になっています。隠れた要素は幅や高さが「0」として扱われるため、その状態でslickが初期化されると、スライダーのサイズ計算が狂ってしまいます。あとからタブを開いて要素が表示されても、初期化時に計算された誤った値が残ったままになり、レイアウトが崩れるというわけです。
解決の基本方針は、「タブが表示されたタイミングで、slickにサイズを計算し直させる」ことです。slickには、レイアウトを再計算するsetPositionというメソッドが用意されているので、タブのクリック時にこれを呼びます。
var slider = $(".slider").slick({});
// タブ切り替え時に、スライドをリセット
$('.tab_box .tab_btn').on("click", function () {
slider.slick("slickGoTo", 0); // 最初のスライドへ
slider.slick("setPosition"); // slickの位置を初期化
});
ここでの主役はsetPositionです。タブが切り替わってパネルが表示状態になった直後にこれを呼ぶことで、slickが正しい幅・高さを計測し直し、レイアウトを組み直します。あわせてslickGoToで最初のスライドへ戻すことで、タブを開くたびに1枚目から見せられ、表示も安定します。
これで、タブ2内のスライダーも崩れずに表示されるようになります。setPositionは「隠れていた要素が表示されたとき」の再計算に効くメソッドなので、タブに限らず、アコーディオンやモーダルの中にslickを入れる場合にも同じ考え方で応用できます。
今回は「先に初期化しておき、タブ切り替え時に再計算する」方法を紹介しましたが、もうひとつのアプローチとして、そのタブが初めて表示されたタイミングで初めてslickを初期化するやり方もあります。非表示のまま初期化しないので、そもそもサイズ計算が狂う余地がなく、根本的な回避策になります。
この場合は、タブのクリック時にそのパネル内のスライダーがまだ初期化されていなければ.slick()を呼ぶ、という判定を加えます。すでにslickが初期化済みかどうかはslick-initializedクラスの有無で確認できます。タブの数が多く、初期表示の負荷を抑えたい場合は、こちらの「表示されてから初期化する」方式が向いています。
display: noneの非表示状態で初期化され、サイズ計算が狂うためsetPositionを呼べば、レイアウトが再計算されて正しく表示されるslickGoToで1枚目に戻すと、開くたびに表示が安定する「表示されていない要素で初期化しない・表示されたら再計算する」——これはslickに限らず、要素のサイズを測って動くライブラリ全般に共通する勘どころです。タブとスライダーを組み合わせる際は、ぜひこの方法を活用してみてください。