【CSS】スクロールの途中から追随する事ができるposition:stickyが効かない時の対処法

追随するメニュー等を作成できる「position:sticky」

例えばメニューなんかで、通常はスクロールをすると当然ですが隠れてしまいますが、そのメニューが追随していれば常にメニューをユーザーに促す事ができるのでインターフェースとしてはかなり有効ですね🤩

昔はこの仕様をわざわざJavasriptで用意していましたが、今はCSSだけで実装可能です。
iframe内でスクロールをしてみてください。

スクロールすると「追随」という部分が途中から追随してきていると思います。(わかりやすいように背景を半透明にしてみました😀)

htmlとcssは下記です。

HTML
<div class="content-body"> <div class="content-body-header"> <h2>ポラーノの広場</h2> <p>宮沢賢治</p> </div> <div class="content-body-sticky">追随</div> <div class="content-body-main"> <p>そのころわたくしは、モリーオ市の博物局に勤めて居りました。</p> <p>十八等官でしたから役所のなかでも、ずうっと下の方でしたし俸給ほうきゅうもほんのわずかでしたが、受持ちが標本の採集や整理で生れ付き好きなことでしたから、わたくしは毎日ずいぶん愉快にはたらきました。殊にそのころ、モリーオ市では競馬場を植物園に拵こしらえ直すというので、その景色のいいまわりにアカシヤを植え込んだ広い地面が、切符売場や信号所の建物のついたまま、わたくしどもの役所の方へまわって来たものですから、わたくしはすぐ宿直という名前で月賦で買った小さな蓄音器と二十枚ばかりのレコードをもって、その番小屋にひとり住むことになりました。わたくしはそこの馬を置く場所に板で小さなしきいをつけて一疋の山羊を飼いました。毎朝その乳をしぼってつめたいパンをひたしてたべ、それから黒い革のかばんへすこしの書類や雑誌を入れ、靴もきれいにみがき、並木のポプラの影法師を大股にわたって市の役所へ出て行くのでした。</p> <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。</p> <p>またそのなかでいっしょになったたくさんのひとたち、ファゼーロとロザーロ、羊飼のミーロや、顔の赤いこどもたち、地主のテーモ、山猫博士のボーガント・デストゥパーゴなど、いまこの暗い巨きな石の建物のなかで考えていると、みんなむかし風のなつかしい青い幻燈のように思われます。では、わたくしはいつかの小さなみだしをつけながら、しずかにあの年のイーハトーヴォの五月から十月までを書きつけましょう。</p> </div> </div>
CSS
.content-body { background-color: #ccc; width: 100%; min-height: 300px; }   .content-body-header { padding:20px; width: 100%; background-color: #eee; }   .content-body-sticky { padding:20px; width: 100%; background-color:rgba(0,0,0,.5); color:#fff; text-align: center; position: sticky; top:0; }   .content-body-main { padding:20px; }   .content-body-main p{ text-indent: 1em; }

「position:sticky」が効かない主な原因

ただこの「position:sticky」、ちょっと癖があって上手くいかない場合がよくあります。その基本的な原因は下記です。

  • 1. topを指定していない
  • 2. 親要素に「overflow:hidden」が指定されている
  • 3. 高さを確保した要素そのものに「position:sticky」をかけている
  • 4. 高さが確保されていない

上記の原因すべてそれぞれ注意が必要です。

先程の例は横並びではなかったのでトラブルになりにくいのですが、よくあるトラブルはflex-boxなんかで横並びになった時に、高さ関連でうまく行かない場合が多いです。

先に横並びで追随する成功例を見てみてください。iframeの中でスクロールをすると右側が途中から追随しています。わかりやすいように右側の背景に斜め線を入れています。

横並びで追随する成功例
HTML
<div class="content-body"> <div class="content-body-header"> <h2>ポラーノの広場</h2> <p>宮沢賢治</p> </div> <div class="content-body-main"> <div class="content-body-left"> <p>そのころわたくしは、モリーオ市の博物局に勤めて居りました。</p> <p>十八等官でしたから役所のなかでも、ずうっと下の方でしたし俸給ほうきゅうもほんのわずかでしたが、受持ちが標本の採集や整理で生れ付き好きなことでしたから、わたくしは毎日ずいぶん愉快にはたらきました。殊にそのころ、モリーオ市では競馬場を植物園に拵こしらえ直すというので、その景色のいいまわりにアカシヤを植え込んだ広い地面が、切符売場や信号所の建物のついたまま、わたくしどもの役所の方へまわって来たものですから、わたくしはすぐ宿直という名前で月賦で買った小さな蓄音器と二十枚ばかりのレコードをもって、その番小屋にひとり住むことになりました。わたくしはそこの馬を置く場所に板で小さなしきいをつけて一疋の山羊を飼いました。毎朝その乳をしぼってつめたいパンをひたしてたべ、それから黒い革のかばんへすこしの書類や雑誌を入れ、靴もきれいにみがき、並木のポプラの影法師を大股にわたって市の役所へ出て行くのでした。</p> <p>あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。</p> <p>またそのなかでいっしょになったたくさんのひとたち、ファゼーロとロザーロ、羊飼のミーロや、顔の赤いこどもたち、地主のテーモ、山猫博士のボーガント・デストゥパーゴなど、いまこの暗い巨きな石の建物のなかで考えていると、みんなむかし風のなつかしい青い幻燈のように思われます。では、わたくしはいつかの小さなみだしをつけながら、しずかにあの年のイーハトーヴォの五月から十月までを書きつけましょう。</p> </div> <div class="content-body-right"> <div class="content-body-sticky"> 追随 </div> </div> </div> </div>
CSS
.content-body { background-color: #ccc; width: 100%; min-height: 300px; }   .content-body-header { padding:20px; width: 100%; background-color: #eee; }   .content-body-main { width: 100%; display: flex; }   .content-body-main p{ text-indent: 1em; }   .content-body-left { padding:20px; width: calc(100% - 100px); }   .content-body-right {   //ここは高さが必要な要素で、position:stickyは設定しない //この要素に高さが必要 width: 120px; background-image: linear-gradient( -45deg, #fff 0%, #fff 25%, #efefef 25%, #efefef 50%, #fff 50%, #fff 75%, #efefef 75%, #efefef 100% ); background-size: 30px 30px; }   .content-body-sticky { padding:20px; background-color:rgba(0,0,0,.5); color:#fff; text-align: center;   //stickyにtopの設定は必須 position:sticky; top:0; }

1. topを指定していない

一番初歩的なミスでは「position:sticky」と同時にtopの設定をしていない場合があります。「position:sticky」ではtopの設定が必須で、上記の場合の数値は0ですが、それでも記述する必要があります。

2. 親要素に「overflow:hidden」が指定されている

これはかなりハマる落とし穴です。というのも、position:stickyを設置した真上の親要素でなく、親要素すべてのどこかにoverflow:hiddenが設定されているだけで機能しない、という現象だからです。私もかなり初期の頃、これでハマった経験があります😫

#wrapper { overflow-x:hidden; }

上のCSSは、別の制作物のお話ですが、クリックすると右から出てくるメニューを制作して、横方向がはみ出ていたので、「overflow-x:hidden」隠していたのですが、それが原因でstickyが効かなくなった、という事がありました😰
原因を探るにの時間かかっちゃった。。。

3. 高さを確保した要素そのものに「position:sticky」をかけている

これもよくある初歩的なミスですが、stickyをさせたい親要素に高さが設定されていないと追随されない。

今回の例でいうと、クラスcontent-body-rightに直接「position:sticky」を設定していないのはそのためです。

4. 高さが確保されていない

先程の内容に付随しますが、親要素に高さが設定されていないと追随されない。実はflex-boxで横並びをした場合はこの部分に少々落とし穴があるので、下で詳しく説明します。

align-itemsの設定のせいでstickyが機能しない

さんざん「高さが必要」と記載しているのに、下記の部分

.content-body-right {   //ここは高さが必要な要素で、position:stickyは設定しない //この要素に高さが必要 width: 120px; background-image: linear-gradient( -45deg, #fff 0%, #fff 25%, #efefef 25%, #efefef 50%, #fff 50%, #fff 75%, #efefef 75%, #efefef 100% ); background-size: 30px 30px; }

↑高さが指定されていないじゃないか!と思ったかたは、ここに注目して欲しい。

.content-body-main { width:100%; display:flex; }

クラスcontent-body-mainに「display:flex」をしているのですが、このdisplay:flexとしている事で、align-itemsは初期値stretchが適用されている。下に「align-items:flex-start」だった場合のhtmlを表示する。

「align-items:flex-start」だった場合
CSS
.content-body-main { width: 100%; display: flex;   //align-items:flex-startだった場合 align-items: flex-start; }

右側の要素、クラスcontent-body-right背景の斜め線が下まで伸びていないのがわかると思う。なので、当然追随もされない。

「align-items:stretch」は、隣り合う子要素の高さに広げる→高さを確保

display:flexの初期値である「align-items:stretch」は、隣り合う子要素の高さに広げる仕様なので、これがstretch以外だった場合は(つまりはflex-start、flex-end、center、baselineだった場合)は高さがないため「position:sticky」が効かない可能性があります🤨
ここも割と落としがちな部分なのでflex-boxで横並びにした場合は注意しましょう!

まとめ

以下、stickyが上手く機能しない時のチェック項目です。

  • 「position:sticky」を利用する場合は0だとしても「top:数値」を指定
  • すべての親要素の「overflow:hidden」を削除する
  • 親要素に高さを確保し、子要素にstickyを設定する
  • flex-boxで横並びの時は「align-items:stretch」にする

特に4つ目は、flex-boxの仕様によるミスなのでチェックしてみてください😀