本記事では、私が過去案件で少しハマったoverflow: hiddenの罠について話していこうと思います。
罠1: overflow-x/overflow-yにhiddenを指定した時になんかうまくいかなくなる
See the Pen trap1-default by tsukaD (@gqzoomig-the-lessful) on CodePen.
上の状態から、「overflow content」のp要素の表示を維持した上で、右側にはみ出た.inner内のインラインテキストを表示させないようにしたかったとします。
See the Pen trap1-hidden by tsukaD (@gqzoomig-the-lessful) on CodePen.
overflow-x: hiddenでいけるやん!……と思って追加したら、横に溢れた文字の非表示はうまく行ったものの、なぜか縦方向にスクロールされるように……😱
解決策
overflow-x: clipを使いましょう
See the Pen trap1-clip by tsukaD (@gqzoomig-the-lessful) on CodePen.
罠2: stickyした時になんかうまくいかなくなる
See the Pen Untitled by tsukaD (@gqzoomig-the-lessful) on CodePen.
overflow: hiddenを指定した親要素.inner内の.header要素にposition: stickyを指定していますが、うまく固定されていません……。
解決策
overflow: clipを使いましょう
See the Pen trap2-clip by tsukaD (@gqzoomig-the-lessful) on CodePen.
overflow:hiddenの罠の正体
雑に「clipを使いましょう」で片付けてしまいましたが、一体なぜ、clipではうまくいくのに、hiddenだとうまくいかないのでしょう?
それは、overflow: hiddenにすると、スクロールコンテナが生成されるからです。
前提として、overflowプロパティには
-
visible(初期値) hiddenclipscrollauto
の5種類の値があります。
そのうち、スクロールコンテナを生成するのは、scroll、auto、そして hidden です。
scrollとautoは、挙動からしてそりゃそうだろうという感じですが、hiddenがスクロールコンテナを生成するというのはなかなかイメージしにくいのではないでしょうか。
……ただ実は、MDNの説明を読むと、hiddenにした時点でスクロールコンテナを生成するのではなく、アンカーやJSによるスクロールが行われた時点でスクロールコンテナを生成するようです。
が、別にこの条件が満たされていなくても、スクロールコンテナを生成していないと説明がつかないような挙動をhiddenはします(特に後述のstickyとの関係など)。スクロールコンテナが生成されている状態とは別に、スクロールコンテナスタンバイみたいな状態がCSS上にあるということなのでしょうか。
この辺は調べてもよくわからなかったので、本記事では便宜上hiddenはスクロールコンテナを生成するものとして扱います。
罠1の正体
これについては、仕様としてはっきりと記載されていました。
overflow-y が hidden、scroll、auto のいずれかで、 overflow-x プロパティが visible (既定値)の場合、この値は暗黙的に auto として計算されます。
つまり、罠1では、overflow-xにのみhiddenを指定してしまっていたから、初期値のままにしていたもう片方のoverflow-yにautoが適用され、縦方向のスクロールバーが勝手に発生してしまっていたというわけです。
なんでこんな仕様にしているかということなのですが、おそらく片方のoverflowのhiddenでスクロールコンテナを生成した時点で、もう片方に非スクロールのvisibleを当てはめることができなくなり、かといって明示的にxにのみhiddenを設定しているのに、勝手にもう片方にもhiddenが当たるのもよくないので、折衷案としてautoにしたのではないかと勝手に想像しています。
(ちなみに、overflow-x: hidden; overflow-y:clipとすると、overflow-yが自動的にhiddenに変更されます。これもスクロールコンテナの兼ね合いだと思われます)
罠2の正体
これは、position: sticky側の仕様を見ると答えが書いてあります。
この値は、常に新しい重ね合わせコンテキストを生成します。なお粘着要素は、直近の祖先がスクロールしない場合でも、「スクロールの仕組み」を持つ直近の祖先(overflow が hidden, scroll, auto, overlay として作成されたもの)に「粘着」します。
つまり罠2では、overflow: hiddenによって.inner要素に対してスクロールコンテナが生成されたため、sticky要素の位置の基準が、本来のビューポートから、「「スクロールの仕組み」を持つ直近の祖先」である.inner要素に更新されるということが起こってしまったわけです。
overflow: clipにすると、スクロールコンテナは生成されませんから、stickyの基準位置も元通りビューポートになります。
まとめ
何かと癖の強いoverflow: hiddenですが、「スクロールコンテナを生成する」ということを頭に入れておけば、少しだけ仲良くなれるのではないでしょうか。
改めて、こういった仕様を理解しておくことは大事だなあと感じます。