開発未分類メモ
CSSでテーブルヘッダーを固定する(position: stickyが効かない時の対処法)
結論
Flexboxレイアウト内で position: sticky が効かない場合、以下の2点を確認する。
- 親要素に
min-height: 0を追加する(Flexboxの高さ制約を伝播させる) - スクロールコンテナを明確にする(
overflow: autoを持つ要素を特定)
/* 親コンテナ(Flexアイテム)*/
.parent {
flex: 1;
min-height: 0; /* これが重要 */
display: flex;
flex-direction: column;
}
/* テーブルを包むコンテナ */
.table-wrapper {
flex: 1;
overflow: auto; /* ここがスクロールコンテナ */
}
/* テーブルヘッダー */
thead {
position: sticky;
top: 0;
z-index: 1;
}
症状
Nuxt/Vueで作成したテーブルコンポーネントで、thead に position: sticky; top: 0 を設定したが、スクロールしてもヘッダーが固定されない。
さらに悪いケースでは、ヘッダー行の上にデータ行が表示されてしまう「ブサイク」な状態になった。
原因
position: sticky の動作条件
position: sticky は、最も近いスクロール祖先(overflow: auto または overflow: scroll を持つ要素)に対して固定される。
スクロール祖先が正しく設定されていないと、stickyは機能しない。
Flexboxと高さの問題
Flexboxレイアウトでは、子要素のデフォルトの min-height が auto になる。これにより、コンテンツの高さがそのまま使われ、親の高さ制約が伝播しない。
[viewport] height: 100vh
↓ 高さ制約
[flex container] flex: 1
↓ min-height: auto だと伝播しない!
[flex item] → コンテンツの高さがそのまま使われる
↓
[table] → スクロールが発生しない → stickyも効かない
解決方法
1. Flex階層に min-height: 0 を追加
高さ制約を伝播させるため、Flexアイテムに min-height: 0 を追加する。
/* Before */
.detail-pane {
flex: 1;
display: flex;
flex-direction: column;
}
/* After */
.detail-pane {
flex: 1;
min-height: 0; /* 追加 */
display: flex;
flex-direction: column;
}
2. スクロールコンテナを設定
テーブルを包むコンテナに overflow: auto を設定し、明確なスクロールコンテナにする。
.simulation-table {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.table-container {
flex: 1;
overflow: auto; /* スクロールコンテナ */
}
3. theadにstickyを設定
スクロールコンテナが正しく設定されていれば、thead のstickyが機能する。
.yearly-table thead {
position: sticky;
top: 0;
z-index: 9;
}
/* ヘッダーセルに背景色を設定(透過防止)*/
.yearly-table thead th {
background: #1f2937;
}
修正前後の比較
Before(効かない)
[.detail-content] overflow: auto
↓ スクロールはここで発生するはず...
[.simulation-table] overflow: visible(デフォルト)
↓ しかし高さ制約が伝播しない
[table]
[thead] position: sticky → 効かない
After(効く)
[.detail-pane] min-height: 0 ← 追加
↓ 高さ制約が伝播
[.simulation-table] height: 100%, overflow: hidden
↓
[.table-container] flex: 1, overflow: auto ← スクロールコンテナ
↓
[table]
[thead] position: sticky, top: 0 → 効く!
デバッグ方法
スティッキーが効かない場合、以下をブラウザのDevToolsで確認する。
// スクロールコンテナの状態を確認
const container = document.querySelector('.table-container');
console.log({
scrollHeight: container.scrollHeight, // コンテンツの高さ
clientHeight: container.clientHeight, // 表示領域の高さ
overflow: getComputedStyle(container).overflow
});
// scrollHeight > clientHeight ならスクロール可能
scrollHeight と clientHeight が同じ場合、スクロールが発生していないため、stickyも機能しない。
まとめ
position: sticky が効かない場合のチェックリスト。
- スクロールコンテナ(
overflow: auto)は正しく設定されているか - Flexboxの親要素に
min-height: 0は設定されているか scrollHeight > clientHeightになっているか(スクロールが発生するか)- sticky要素に背景色は設定されているか(透過して見えていないか)