質問: position stickyを使って、スマホwebサイトで上部固定をしております。 上部固定された瞬間にstickyした要素内のスタイルを変えたりしたいのですが、固定になった瞬間を検知できる方法を探しております。 javascriptでスクロールで検知する方法以外に何かのステータスで検知できないものかと。 スクロールで検知するなら、結局fixed使う時と変わらないので。。 もし何かご存知の方がいらっしゃったらご教授願いたいです。
回答: 現在のところ、`position: sticky`を設定されている要素が固定状態になったかどうかを直接検出する方法はありませんが、**IntersectionObserver**を用いて行う方法があります。この記事で紹介されているのもIntersectionObserverを用いた方法ですが、この回答ではよりシンプルな設定のバージョンを用意してみました。 IntersectionObserverは、簡単に言えば、「特定の要素が画面内にあるかどうかを検出することができる機能」です。(本当は画面内ではなく特定の要素内など、細かい設定がいろいろ可能ですが。) 以下のサンプルをご覧ください。 codepen.io ``` // 後で操作する用に固定部分の要素を取得 const header = document.querySelector('header'); const observer = new IntersectionObserver(entries => { for (const entry of entries) { // entry.isIntersectingは // 赤い半透明の部分がわずかでも画面に入っていればtrue if (entry.isIntersecting) { // 固定されていない場合の処理 header.classList.remove('fixed'); } else { // 固定されている場合の処理 header.classList.add('fixed'); } } }); observer.observe(document.getElementById('header-before')); ``` ``` p.content { writing-mode: vertical-lr; margin: 1em 0; white-space: nowrap; } /* 固定部分のスタイル */ header { position: sticky; left: 0; top: 0; background-color: #444444; color: #ffffff; padding: 6px; font-weight: bold; } /* 固定部分がページ上部に固定されているときのスタイル (JavaScriptからクラスを付与) */ .fixed { background-color: #440088; } /* ヘッダーの前後のダミー要素 */ #header-before { position: absolute; left: 0; top: -33px; width: 100%; height: 32px; background-color: rgba(255, 64, 64, 0.3); pointer-events: none; } ``` ``` sticky要素の上部固定を検知するサンプル 前書き前書き前書き 固定部分 すごく縦に長いページ内容。もしPC画面のブラウザなどでこれでも長さが足りない場合はウィンドウを小さくしたり開発者ツールを出したりしてみてください ``` このサンプルでは、`position:sticky`が設定された要素の上に赤く半透明な領域を用意してあります。よくよく考えてみれば、固定領域が画面上部に固定されている状態というのは、赤い半透明な領域がスクロールされて画面上部に完全に消えた状態に相当します。 ですから、赤い半透明な領域が画面内に見えているかどうかを検知することによって、固定領域が画面上部に固定されているかどうかの状態変化を検知することができます。具体的には、赤い半透明の領域が画面内に見えているときは固定されていない、 画面内に見えていないときは固定されていると判断すればいいのです。 (この判断方法だと、赤い半透明の領域がまだページの下の方にあるときも固定されている判定になってしまいますが、その時は`position:sticky`部分もまだ見えていないので問題ありません。もしそれだと問題があるという場合は、固定領域の下にも同じような領域を用意するとよいでしょう。) もちろん実際に実用するときは、半透明な領域は完全に透明にしてしまいましょう。 IntersectionObserverの使い方がよく分からない場合は、日本語記事もありますので調べてみるのがよろしいかと思います。 なお、`position:sticky`は主要ブラウザは全て対応しているのに対して、IntersectionObserverはまだSafariのサポートがありません。 もしSafariでも動作させたい場合はPolyfillが必要になります。このPolyfillは恐らく結局スクロールイベントを監視することになりますが、それ以外のブラウザでIntersectionObserverを使えば動作時の負荷を削減することができ、十分益があるのではないかと思います。