あまり機会がなく、すぐ忘れてしまうので備忘録として。
うまくいかないケース
下記のようなHTMLにおいて、CSSで.container
にoverflow:scroll
、(.fix
)にposition:fixed
を設定した時に、思った結果にならない。
<div class="container">
<nav class="fix">ナビゲーションなど</nav>
<p>長い文章〜(略)〜長い文章。</p>
</div>
body {
padding: 100px 0;/* わかりやすいように隙間を開ける */
}
.container {
border: 1px solid #ccc;
width: 30em;
height: 5em;
overflow: hidden scroll;
}
nav.fix {
position: fixed;
top: 0;
left: 0;
width: 100%;
background-color: #eee;
}
期待
-
.container
の上部にnav.fix
を固定したい -
.container
をスクロールしてもposition:sticky
のように、そのままの位置に固定したい
実際
-
nav.fix
が画面(ビューポート)の上部に固定されてしまう -
.container
をposition:relative
としてもうまくいかない(position:fixed
な子孫要素の包含ブロックにはならない)
.container
を包含ブロックとして機能させられればいいのだが、そのためにはいくつかある条件の1つを満たす必要がある。
position:sticky
を使う
たいていの場合はposition:fixed
でなく、position:sticky
で対応すればいい。
stickyを使ったサンプル
<div class="container">
<nav class="fix">ナビゲーションなど</nav>
<p>長い文章〜(略)〜長い文章。</p>
</div>
body {
padding: 100px 0;/* わかりやすいように隙間を開ける */
}
.container {
border: 1px solid #ccc;
width: 30em;
height: 5em;
overflow: hidden scroll;
}
nav.fix {
- position: fixed;
+ position: sticky;
top: 0;
left: 0;
width: 100%;
background-color: #eee;
}
何らかの理由でposition:fixed
でなければならない時
特にposition:sticky
のような、粘着する動作を実現したい時には、HTMLの構造を変えるなどの工夫が必要。
まず、スクロールする要素を.content
、fixedする要素を.fix
、その2つを包含する要素を.container
とする。
<div class="container">
<nav class="fix">ナビゲーションなど</nav>
+ <div class="content">
<p>長い文章〜(略)〜長い文章。</p>
+ </div>
</div>
body {
padding: 100px 0;/* わかりやすいように隙間を開ける */
}
.container {
border: 1px solid #ccc;
width: 30em;
height: 5em;
- overflow: hidden scroll;
}
+ .content {
+ width: 100%;
+ height: 100%;
+ overflow: hidden scroll;
+ }
nav.fix {
position: fixed;
top: 0;
left: 0;
width: 100%;
background-color: #eee;
}
(ここで、.container
自体をスクロールできるようにすると、次項の処理を行なってもposition:sticky
のように粘着する動作にならず失敗する。)
最終形
これだけだと、.fix
はビューポートを基準に配置されてしまい、ページの最上部に固定されてしまうので、.container
が包含ブロック1となるように.container
のスタイルにtransform:scale(1)
(など)を追加する。
<div class="container">
<nav class="fix">ナビゲーションなど</nav>
<div class="content">
<p>長い文章〜(略)〜長い文章。</p>
</div>
</div>
body {
padding: 100px 0;
}
.container {
border: 1px solid #ccc;
width: 30em;
height: 5em;
+ transform:scale(1);
}
.content {
width: 100%;
height: 100%;
overflow: hidden scroll;
}
nav.fix {
position: fixed;
top: 0;
left: 0;
width: 100%;
background-color: #eee;
}
思いがけず包含ブロックになってしまうケース
別の事例で、思いがけず包含ブロックになってしまい、思いがけない結果になるケース。
<div class="content">
<p>文章〜(略)〜文章</p>
<nav>
<button>Item 1</button>
<button>Item 2</button>
<div class="close">×</div>
</nav>
</div>
.content {
position: relative;
border: 1px solid #ccc;
}
nav {
background-color: #eee;
filter: drop-shadow(3px 3px 3px rgba(0, 0, 0, 0.5));
}
.close {
position: absolute;
top: 4px;
right: 4px;
}
.close
を.content
の右上に配置するつもり。
しかし、nav
にfitler
プロパティが設定され、値がnone
以外であるため、nav
が包含ブロックになってしまう。
結果、.close
はnav
の右上に表示される。