【JavaScript】Intersection Observerでスクロール処理をする

## やりたいこと
Intersection Observerをつかって要素連動の処理を実装する。

すでに、他のライブラリを読み込む数が多かったり、サイトの処理が重い場合などで、更に追加でGSAPなどを読み込むの敬遠される背景がある。

今回は、画面幅＝ビューポート内に対象要素が入ってきたら文字色の変化を加える処理をする。

__実装サンプル__
![アニメ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883475/b1e649c7-df2a-f477-67d3-d87a74ac93ac.gif)

https://codepen.io/frontnikki/full/wvxVygm


※Codepenでは思ったタイミングでコールバック関数が発火してくれず、何か原因がわかる方いらっしゃれば教えて頂きたいです。。。



## コード
```JavaScript
const target = document.querySelector('.article__title--target');

//監視設定
const settings = 
  rootMargin: '-50% 0px',
  threshold: [0],
}

//IntersectionObserverコンストラクタでインスタンス化
const observer = new IntersectionObserver(callback,settings);
observer.observe(target);

//コールバック関数
function callback(a,b) {
  for(const value of a){
    if(value.isIntersecting === true){
      value.target.classList.add('is-inviewport')
    }else{
      value.target.classList.remove('is-inviewport')
    }

  }
}
```

:::note info 
__Intersection Observerを使用するときのお作法__

1. 対象要素を監視する画面範囲、対象要素への変更を加えるタイミング、対象要素の監視間隔の設定オブジェクト
2. IntersectionObserverコンスタクタでインスタンス化する。
第一引数にコールバック関数、第二引数に1で設定した設定のオブジェクトを渡す。
3. observeメソッドで監視開始
:::


## 設定オブジェクトに関して
> __rootプロパティ__
>ターゲットが見えるかどうかを確認するためのビューポートとして使用される要素です。指定されなかった場合、または null の場合は既定でブラウザーのビューポートが使用されます。
>
>__rootMargin__
root の周りのマージンです。 CSS の margin プロパティに似た値を指定することができます。例えば、”10px 20px 30px 40px" (top, right, bottom, left) のようなものです。この値はパーセント値にすることができます。この一連の値は、交差を計算する前にルート要素の範囲のボックスの各辺を拡大または縮小させることができます。既定値はすべてゼロです。
>
>__threshold__
単一の数値または数値の配列で、ターゲットがどのくらいの割合で見えている場合にオブザーバーのコールバックを実行するかを示します。見える範囲が 50% を超えたときのみ検出する場合は値 0.5 を使用します。 25% を超える度にコールバックを実行する場合は、 [0, 0.25, 0.5, 0.75, 1] という配列を指定します。既定値は 0 です（つまり、 1 ピクセルでも表示されるとコールバックが実行されます）。 1.0 の値は全てのピクセルが見えるようになるまで、閾値を超えたとはみなされないことを意味します。
>
>https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API の引用

### コールバック関数に関して
コールバック関数の第一引数にはIntersectionObserveEntryオブジェクトが渡される。
isIntersectionプロパティやtargetプロパティがよく使われるプロパティらしい。
![スクリーンショット 2023-02-11 22.47.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883475/32cfb209-8180-ec12-7742-8aa9790274d6.png)


第2引数にはIntersectionObserverオブジェクトが与えられる。中身を見ると、設定したrootMarginやthresholdsプロパティ、ここでは設定していないrootプロパティがnullで返されている。
![スクリーンショット 2023-02-11 22.49.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883475/34bd3253-2c4a-879c-0f7e-42aea965184e.png)

## コード解説
### 設定オブジェクト
``` JavaScript
const settings = 
  rootMargin: '-50% 0px',
  threshold: [0],
}
```

`rootMargin`プロパティで-50% 0pxを指定。

`root`プロパティを用意していないので、監視対象の反映は画面＝ビューポートとなる。

`rootMargin`で-50%と指定しているため、対象要素の上辺がビューポートの半分を通過した時点で処理は始まり、対象要素の下辺がビューポートの半分を通過した時点で別の処理が走る。

`threshold`プロパティで0を指定しているが、これは`root`プロパティで指定した要素に対して交差している割合を示し、そのときにコールバック関数が発火する。

したがって、コールバック関数が発火するタイミングは

:::note info
対象要素の上辺もしくは下辺が真ん中に差し掛かったとき。
スクロールの方向によって対象要素の上辺なのか下辺なのかが変わる。
:::


### コールバック関数
```JavaScript
function callback(a,b) {
  for(const value of a){
    if(value.isIntersecting === true){
      value.target.classList.add('is-inviewport')
    }else{
      value.target.classList.remove('is-inviewport')
    }
  }
}
```

要素が設定した対象範囲に入ってきたかどうかの判定をしている。

入ってきたらis-inviewportクラスを付与して文字色を変えて、監視対象画面から外れたらクラスを外す定番の処理。

### インスタンス化して監視対象を監視
``` JavaScript
const observer = new IntersectionObserver(callback,settings);
observer.observe(target);
```

## まとめ
今回は、案件の背景上なるべく他プラグインを導入しないほうが良いとの判断でバニラJSで完結するIntersection Observerを実装してみた。

GSAPを使用したことがあれば、__コールバック関数の引数に何が与えられるのか__ と __第二引数に渡す設定オブジェクトの内容__ を知っていればあまり抵抗なく使えると思ったので、使い場所はあると思う。

サイトのパフォーマンスに大きな影響を与えることがなく、特殊プラグインの読み込みもないため尚良しだと思った。

## 参考文献
https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API


https://ics.media/entry/190902/