JavaScriptのIntersectionObserverAPIでスティッキーナビを実装する方法
JavaScriptのIntersectionObserverでスティッキーナビゲーションを実装する方法を紹介します。
window.addEventListenerのscrollよりもパフォーマンスが改善されるメリットがあります。
window.addEventListenerのscrollでのスティッキーナビゲーションでの実装方法と比較してみましょう。
window.addEventListenerのscrollでのスティッキーナビゲーション
//スクロール先の定数
const section1 = document.querySelector('#section--1');
//scrollイベントで設定する(パフォーマンス悪い)
const initialCoords = section1.getBoundingClientRect();
// console.log(initialCoords);
window.addEventListener('scroll', function () {
// console.log(window.scrollY);
if (window.scrollY > initialCoords.top) nav.classList.add('sticky');
else nav.classList.remove('sticky');
});
スクロールイベントはdocumentではなく、windowにイベントハンドラーを設定します。
そして、window.scrollYとするとスクロールしたときのトップからのY座標を確認できます。
そして、特定の場所のY座標はgetBoundingClientRect()のtopを確認するとわかります。
そのため、スクロールしたY座標が特定の場所のY座標を超えたら、クラスを追加してあげればOKです。
これでも動きますが、scrollイベントはパフォーマンスがとても悪いです。その理由は、動くたびにスクロールの座標が取得されてしまうためです。モバイル環境では特におすすめできません。
intersection observerとは
intersectionとは、物体の交差という意味です。
observerとは、監視するものという意味です。
intersectionObserverは、物体の交差を監視するものという意味です。
intersection observer APIの基本的な使い方
// 交差を監視したいもの
const section1 = document.querySelector('.section-1');
// 交差したときに呼び出すコールバック関数
const cb = function() {
console.log('交差しました');
};
// IntersectionObserverの初期化
const io = new IntersectionObserver(cb);
// 監視を開始する
io.observe(section1);
- 交差を監視したいもの設定する
- 交差したときに呼び出すコールバック関数を作成する
- IntersectionObserverの初期化する
- 監視を開始する
という流れが基本となります。
new演算子でIntersectionObserverにコールバック関数を渡して初期化します。
それを変数に格納し、監視したいDOMを登録することで監視を開始できます。
監視を開始するとき、observe()メソッドを使います。
このようにすることで、section-1というクラスをもった要素が画面内に入るときと出るときに、毎回IntersectionObserverに設定したコールバック関数が呼ばれます。
intersection observer APIの具体的な使い方
//スクロール先の定数
const section1 = document.querySelector('#section--1');
//コールバック関数
const obsCallback = function (entries, observer) {
entries.forEach(entry => {
console.log(entry);
});
};
const obsOptions = {
root: null, //rootプロパティ, nullでブラウザーのビューポートを使用することができます。
threshold: 0.1, //しきい値。0.1は10%の意味です。
};
//IntersectionObserver
const observer = new IntersectionObserver(obsCallback, obsOptions);
observer.observe(section1); //このsection1がターゲットとなります。
IntersectionObserverの初期化
intersection observer APIは、new IntersectionObserver(コールバック関数, オプション)とすることで初期化できます。
上の例では、それをobserverという変数に格納しています。
IntersectionObserverのコールバック関数
コールバック関数では、entriesとobserverという2つの引数をとります。
observerはIntersectionObserverの初期化したものです。
entriesについて使い方を詳しくみてみましょう。
コールバック関数の第一引数entries
entriesについて、IntersectionObserverには、次のように複数の要素を登録することができます。
observer.observe(section1); //このsection1がターゲットとなります。
observer.observe(section2); //このsection2がターゲットとなります。
observer.observe(section3); //このsection3がターゲットとなります。
複数の要素を登録したときは、それぞれの要素が画面内に出たときと入ったときにコールバック関数が呼ばれることになります。それぞれに処理するためにforEach()で処理を記述します。
各々をentryとして、isIntersectingというプロパティを使って、trueの場合は要素がある状態という意味になります。
isIntersectingプロパティがfalseのときは、監視から外れたときを意味します。
//コールバック関数
const obsCallback = function (entries, observer) {
entries.forEach(entry => {
if(entry.isIntersecting) {
console.log(`${entry}が画面にあります`);
// 1回出たら監視をやめたいとき
observer.unobserve(entry.target);
} else {
console.log(`${entry}が画面から出ました`);
}
});
};
1回監視をしたら、それ移行は監視をやめたいという場合、unobserve()メソッドに監視対象(entry)のtarget(section-1)を渡します。
entriesは常に配列
entriesはthresholdの配列となります。
たとえ値が一つでも配列になります。
IntersectionObserverのoptionsは複数のthresholdを保持できるためです。
このように、addEventListenerのscrollとは違い、交差した時点でのみ反応させることができます。
IntersectionObserverのオプション
Intersection observerのオプションを設定することで、コールバック関数を制御できます。
Intersection observerのオプションには、次の3つのを設定します。
- root
- threshold
- rootMargin
root:nullでビューポート
rootはターゲットを観測するためのビューポートの設定です。
交差対象とした親や先祖の要素を設定できます。親や先祖の要素を設定すると、監視対象と設定した親や先祖要素が交差した時点でコールバック関数を呼び出すことができます。
nullもしくは指定をしないことで、ブラウザーのビューポートを使用することができます。実際はnullでの設定になることが多いでしょう。
上の場合、ターゲット要素であるobserver.observe(section1)がroot、つまりビューポートの10%(thresholdの値)に入ったらobsCallbackを実行することができます。
スクロールアップでもスクロールダウンでも実行されます。
thresholdはしきい値
thresholdはしきい値・閾値の意味です。限界とか境界といった意味に近いです。
0〜1を指定します。スクロールアップでもスクロールダウンでも、監視対象エリアに監視対象が最初に入る部分を0、監視対象が最後には入る部分が1です。
オブザーバーのコールバック(この場合はobsCallback)を実行するターゲットがどのくらいの割合で見えているかを%で示します。
0.1は10%の意味です。監視対象のものは、ブラウザに10%入ったらコールバック関数が呼び出されます。
console.log(entry)でIntersectionObserverEntryを取得できます。その中のintersectionRatioを確認すると0.1前後であることが確認できます。
thresholdは配列で指定することもできます。[0, 0.5, 1]といった具合です。監視対象がとても大きい場合は配列にすることで、それぞれの地点でコールバック関数を呼び出すことができます。
rootMargin
rootMarginはCSSのmarginのようなものです。pxや%が使えます。
表示を早めたいときや、遅くしたいときに利用します。
プラスの値を設定すると、Y軸で上方向にmarginが設定されます。
設定するときの注意点としては、値が一つの場合上下左右が設定されてしまい、左右のマージンが原因で要素が観測されない場合があるので、
rootMargin: “300px 0px"とするなど、意図した挙動となるように設定しましょう。
intersection observer APIを使ったスティッキーナビゲーション
//targetを定義
const header = document.querySelector('.header');
//rootMarginを決めるために高さを取得
const navHeight = nav.getBoundingClientRect().height;
//コールバック関数
const stickyNav = function(entries) {
//entriesは常に配列
const [entry] = entries;//entries[0]と同じ意味
console.log(entry);
//Guard clause(Guard Clause ガード節,ガード条件)
// targetであるheaderがroot、つまりビューポートから見えなくなったらstickyをつけます。
if(!entry.isIntersecting) nav.classList.add('sticky');
else nav.classList.remove('sticky');
}
//IntersectionObserverの設定、今回はoptionsをそのまま記載
const headerObserver = new IntersectionObserver(stickyNav, {
root: null,
threshold: 0, //headerが完全に見えなくなったらスティッキーにしたい
rootMargin: `-${navHeight}px`,
});
//呼び出し
headerObserver.observe(header);
IntersectionObserverの設定は、今回はoptionsをそのまま記載しています。
targetであるheaderがroot、つまりビューポートから見えなくなったらstickyをつけて、headerがビューポートで見えるようになったらstickyを外すというコールバック関数です。
entriesの中身は一つですが、配列なので上のような処理になります。
rootMarginはrootつまりビューポート内でターゲットであるheaderがみえなくなると判定するY軸の基準点を調整できます。マイナスなら上に、プラスの値なら下にずらせます。
上の例のように、thresholdを0にして、rootMarginで調整するといいでしょう。
rootMarginの高さは、ナビゲーションのgetBoundingClientRect().heightで取得できます。
画面幅で変わるため、このように設定するのが望ましいです。
IntersectionObserverのthis
IntersectionObserverはwindow.IntersectionObserverなのでthisはwindowを指します。
うまく動かない場合、
new IntersectionObserver(callback.bind(this), this.options);
上のようにbind(this)などでthisの参照先をコントロールしましょう。