- overflow は、何を前提にしているか スクロールが生まれる条件
- ブロック要素は、そもそも広がらない width:auto の性質
- フレックスの子は、縮まない min-width:auto という既定値
- overflow-y だけ書くと、横も auto になる 計算値の伝播
- width:max-content が要る場合と、無駄な場合 中身がブロックかどうか
- まとめの早見表 症状から引く
overflow は、何を前提にしているか
overflow-x:auto は「はみ出したらスクロールさせる」指定です。
ここで見落としやすいのが、「はみ出す」には要素の幅が先に決まっている必要があることです。
つまり overflow を効かせるには、その要素が
中身より小さくなれることが前提になります。これを本記事では「縮む余地」と呼びます。
縮む余地が無くなる原因は、大きく2つでした。 ① 要素自身が中身に合わせて広がる、 ② 中身が箱の幅に合わせてしまい、差が生まれない。 順に見ていきます。
ブロック要素は、そもそも広がらない
まず安心できる話から。通常のブロック要素は width:auto が既定で、
これは親の内容幅に一致します。中身がどれだけ長くても、箱は広がりません。
.box {
overflow-x: auto; /* 親幅に収まり、中身がはみ出せばスクロール */
}
ですから「ブロック要素に overflow-x:auto を書いたのに効かない」なら、
原因は別のところにあります。§3以降のどれかです。
min-width:0 は書いておく価値があります。
その要素が将来フレックスやグリッドの子になった瞬間、§3の問題が発生するからです。
ブロックのままなら書いても何も変わりません。
フレックスの子は、縮まない
ここが本題です。フレックスアイテムの min-width は既定が auto で、
これは「中身の最小幅より小さくならない」という意味になります。
.bar { display: flex; }
.bar .path {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
/* ↑ 3点セットを書いても、省略されない */
}
white-space:nowrap があるので中身の最小幅は一行ぶんの実寸です。
min-width:auto がそれを下限にするため、
箱は縮めず、中身と同じ幅になります。差がゼロなので省略記号も出ません。
.bar .path {
min-width: 0; /* 縮む余地をつくる */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
overflow:hidden も text-overflow も書いてあるのに効かず、
原因が分かるまで何度も別の場所を疑いました。
display:grid の子要素も min-width:auto になります。
さらに縦方向では min-height:auto が同じ働きをするので、
縦スクロールが効かないときは min-height:0 を疑ってください。
overflow-y だけ書くと、横も auto になる
縦だけスクロールさせたい箱を作ったつもりが、横にもスクロールできる状態になっていました。
.list {
max-height: 230px;
overflow-y: auto;
/* overflow-x は書いていない → visible のつもり */
}
overflow-x と overflow-y の片方が
visible 以外のとき、もう片方の visible は
計算値として auto に変わります。
理由は単純で、片方だけを見えるままにする描画ができないからです。
縦にクリップしながら横だけ枠外へ描く、ということが成立しません。
そこで visible は auto へ倒されます。
.list {
max-height: 230px;
overflow-y: auto;
overflow-x: clip; /* 横は塞ぐ、と書く */
}
max-height + overflow-y:auto で作ったところ、
中身に white-space:nowrap の長い行があり、
横方向のスクロール領域が生まれていました。
縦だけのつもりだったので、しばらく気づけませんでした。
width:max-content が要る場合と、無駄な場合
箱に縮む余地があっても、まだスクロールしないことがあります。 今度は中身の側に理由があります。
中身がブロック要素だと、差が生まれない
.wrap { overflow-x: auto; }
.wrap .line {
/* div などのブロック要素 */
white-space: pre; /* 折り返さない */
}
.line はブロックなので width:auto = 親幅です。
中身のテキストは枠外へ描かれますが、要素自体の幅は親と同じ。
箱と中身の差がゼロなので、スクロール領域が生まれません。
.wrap .line {
width: max-content; /* 中身の実寸まで広がる */
min-width: 100%; /* 短い行でも枠いっぱい */
white-space: pre;
}
ところが、書いても無駄な場合がある
.wrap pre {
overflow-x: auto;
width: max-content; /* max-width に負けて無効 */
max-width: 100%;
}
width:max-content が要るのは、スクロール容器の中に
別のブロック要素があり、それが幅を持てないときだけです。
容器自身が pre のように直接テキストを持つなら不要——
むしろ max-width:100% と競合して混乱のもとになります。
div + white-space:pre でログ行を組んだ箇所で、
親に overflow-x:auto を書いても効きませんでした。
逆に pre を使っている箇所へ同じ指定を足したところ、
そちらは最初から不要だったと後で分かりました。
5つのパターンを、並べて見る
画面幅360px、中身の実寸900pxという条件で、箱と中身がどう振る舞うかを比べます。
2番目だけ、箱そのものが画面をはみ出しています。 フレックスアイテムが縮めないため、ページ全体の幅を押し広げる—— これが最も見つけにくいパターンでした。
まとめの早見表
| 症状 | 疑うところ | 対処 |
|---|---|---|
| overflow-x:auto が効かない | 中身がブロック要素 | 中身に width:max-content |
| text-overflow:ellipsis が効かない | フレックスの子 | min-width:0 |
| 箱が画面をはみ出す | フレックス/グリッドの子 | min-width:0 |
| 意図しない横スクロール | overflow-y だけ指定 | overflow-x:clip を明示 |
| 縦スクロールが効かない | フレックスの子(縦方向) | min-height:0 |
| max-content を書いても変わらない | max-width:100% と競合 | そもそも不要な場面 |
この記事のまとめ
- スクロールは「箱の幅」と「中身の幅」の差から生まれる。差が無ければ overflow は働かない。
- ブロック要素は
width:auto= 親幅。そもそも広がらないので安全。 - フレックス/グリッドの子は
min-width:auto。中身より小さくなれない。 overflow-yだけ指定すると、横のvisibleはautoに変わる。width:max-contentは中身がブロックのときだけ。max-width:100%があると打ち消される。- 予防として
min-width:0を書いておくと、後でフレックスの子にしても壊れない。
- 縮まないと、あふれる(この記事)
min-width:0 と overflow の関係 - クリップとスクロールの境界
hidden / clip / overscroll-behavior - 背景と合成レイヤー
::before / fixed / will-change