備忘録

【CSS】sticky要素のY方向センタリング

sticky要素をY方向センタリングすると、途中までいい感じで追従してくるにもかかわらず、最後でメイン要素よりも先に追従をやめてしまう。

この謎の現象の原因は、top:50vh, transform:translateY(-50%)でした。

問題を引き起こしたコード

See the Pen
sticky error code
by mikiya (@mikiya)
on CodePen.

スクロールしていただければ、黄色いブロックが最後まで追従しないことが分かるかと思います。

sticky要素は、親の領域内でしか移動できない

分かっているつもりでしたが、ここに盲点がありました。

試しに、transform:translateY(-50%)を外してみると、原因が分かります。

htight>50vhの要素に対してtop:50vhを指定しているため、最後差分のスクロールは追従が終了しています。

その状態でtranslateY(-50%)が働くため、初めの現象が発生してしまっていました。

解決策

JavaScriptでセンタリング

See the Pen
sticky demo (jQuery)
by mikiya (@mikiya)
on CodePen.

※jQueryを使用しています。

このように、JavaScriptで計算し、cssのプロパティを直接修正すれば、1番下までスクロールしても大丈夫です。

しかし、css()でtopを指定すると、スクロールごとにリフローが発生してしまうため、あまりおすすめできる方法ではありません。

せめてthrottle等の併用はした方が良いと思います。

position:sticky指定しない

See the Pen
sticky demo (use fixed)
by mikiya (@mikiya)
on CodePen.

navをposition:fixedで固定し、mainをnavのwidth分だけtranslateXすれば、cssのみでセンタリングできます。

(padding-left等、他の指定の方が適しているかも)

ですが、stickyではないので、headerやfooterがあっても貫通してしまいます。

top:0で固定したsticky要素の内側に、センタリングする要素を作る

See the Pen
centering demo (recommend)
by mikiya (@mikiya)
on CodePen.

jQueryからscrollイベントが消えていることが分かるかと思います。

方法としては、

  1. navを丸ごと囲む要素(nav-wrapper)を新たに作り、これをtop:0のsticky要素とする
  2. navはnav-wrapperにbottom固定しておく
  3. jQueryで先ほどと同様にnavTopを求め、これとnav自身の高さを足し合わせたものをnav-wrapperの高さに指定すれば、navはセンタリングされる

といった感じです。

ここで③のjQueryを使用した作業はscroll時には不要なので、負荷が軽減できます。

ただし、headerを挿入した際に今度は最初の追従開始が遅れることと、余計なhtml要素が増えることには注意が必要です。

結論

センタリングしたいなら、無理にsticky使おうとしない方が良いのかも。

なお、stickyする要素のheightが50vh以下であれば、最初の問題を引き起こしたコードでも、問題は発生しません。

ですが、結果的にはクライアント側の環境に依存することになってしまいます。