jQuery プラグインのslickは、簡単にスライダーが表示できてとても便利であるが、先日仕事でslickを使った際にちょっとしたトラブルが発生した。
##slickイベント「setPosition」とは
タブやモーダルなど、ページ読み込み時に非表示になっているコンテンツ内でslickをそのまま使うと高さが取得されずにheight: 0px
となり、領域が潰れて表示されないという問題がある。
この問題を回避するために、slickはsetPosition
というイベントを用意している。setPosition
は、位置やサイズが変更された時に再取得を行うイベントである。
/* タブをクリックした際に発火
.tab クリックするタブ
.slider 対象のスライダー */
$(".tab").on("click", function () {
$(".slider").slick("setPosition");
});
このイベントによって、タブ切り替えで非表示コンテンツが表示になった時も、要素の高さが取得できるようになる。
しかし、slick対象にsetPosition
を指定したにもかかわらず、なぜか効かないという状況が発生した。slickスライダーを含むコンテンツをタブ切り替えすると、コンテンツ内には何も表示されずコンテンツ高さが本来よりも大きくなるという現象だった。
焦って「slick setPosition
効かない」で検索してみても、setPosition
イベントを追加するだけで無事解決!(上記の3行を添えて)みたいな記事しかヒットしない。まるで、「setPosition
で解決しない」状況が存在しないかのようだ。
とはいえ、setPosition
で解決できない状況が実際に目の前で起こっていた。
setPosition
が効かない状況および現象
状況と現象を整理すると以下になる。
状況:タブコンテンツとslickする要素の関係が親と曾孫
現象:タブを切り替えると非表示だったコンテンツの高さが大きくなる
当初は、タブコンテンツとslickする要素が親-子ではなく親-曾孫と、階層が深くなっているためにsetPosition
が効かないのではないかと疑った。しかし、コンテンツの高さが潰れるどころか逆に大きくなっている点が、単なる「setPosition
効かない」問題ではないように思えた。
setPosition
が効かない原因
slickが適用されているスライダー要素をchromeデベロッパーツールで確認すると、CSSのプロパティにdisplay: flex; flex-wrap:wrap;
が付与されていた。
flexで折り返しの横並びにしたリストを、そのままslickのスライドコンテンツとして突っ込んでいたため、このようなCSSプロパティが付いていたのだった。
flexとslickの干渉…?flexによる何らかの影響(何?)で、スライダーに謎の余白が生じていたようである。
こうしてflexに対する疑惑が深まったため、スライダー要素からflexプロパティを取り除いた上でsetPosition
を適用すると、タブ切り替えで非表示コンテンツが表示されるようになった。無事解決したのはよかったが、それまでに4~5時間ほど費やすことになった。
補足1
slick.cssに記述されている.slick-slider
(slick要素に付与されるclass)のプロパティは以下のようになっている。
.slick-slider {
position: relative;
display: block;
box-sizing: border-box;
...
}
このdisplay: block;
がdisplay: flex;
に上書きされることで影響が出たと考えられる。ただ、flexのどのような特性が影響してsetPosition
が効かないのかは解明できていない。
補足2
display:flex;
を付与したスライダー(Sample #1)と付与しないスライダー(Sample #2)、それぞれにsetPosition
を適用したサンプルを作成してみた。
Sample #1は、スライダーの高さが取れない「従来の」setPosition
が効かない現象で、仕事で遭遇した「コンテンツの高さが大きくなる」現象は再現できなかった。何か別の要因が関わっていたのかもしれない。
まとめ
とりあえず、slickさせる要素にflex関係のCSSプロパティが付いていたら直ちに取りましょう。いや、flex をかけたコンテンツをそのままスライダーにぶち込むにするのは止しましょうと言うべきか。
ネットで解決方法を探っても見つからなかったのは、今までそういう無茶なことをする人が居なかったからでしょうか。