Intersection Observer メモ

Intersection Observer

参考サイト

https://tsuruchanblog.com/fixed-header-used-intersection-observer-api/

https://blog.jxck.io/entries/2016-06-25/intersection-observer.html

https://qiita.com/yamanoku/items/027308e23cfc69845d7e

https://yumegori.com/scroll-animation-intersection-observer#chapter-6

https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API

```js:基本(一番単純な例)
//1.インスタンスの生成 コールバック関数の登録

        const observer = new IntersectionObserver(func);

//2.監視要素を登録
           
       observer.observe(document.querySelector('.mainvisual'));

//3. 1で登録するコールバック関数
//   funcの第一引数にIntersectionObserverEntryオブジェクトの配列が渡る
      function func(entries){
         //entries.forEach(e=>{});
           for(e of entries){
              if(e.isIntersecting){
                console.log(e.intersectionRatio);
                e.target.classList.add('showTime');
              }else{
                console.log(e.intersectionRatio);
                e.target.classList.remove('showTime');
              }
           }
       }

// これで監視要素が領域（デフォルトではブラウザ)に入った時と出た時に
// 登録したコールバック関数が発火する。
// よって、発火の条件(optionで変更可)を満たしたときは
//  intersectionRatio は true(入った時) か false(出た時) になります。
```
```js:注意点-監視要素が複数ある場合
//2.監視要素を登録      
       //observer.observe(document.querySelector('.mainvisual'));
   sections.forEach(section=>{

//1つ1つ登録する必要がある。
            observer.observe(section);
   });

// これはできない observer.observe(sections);
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/d20370e5-e451-a708-9467-943a7c8fca67.png)

サンプル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/de0bf33a-601a-81ba-8be4-55712d8b2c37.png)


- 監視要素はメイン画像
- メイン画像が画面にあるかないかを判断して
- ない場合はheader要素にfixedクラスをaddする
- ある場合はheader要素のfixedクラスをremoveする

```html:
    <div id="mainvisual">
        <img src="https://source.unsplash.com/1600x900/?dogs" alt="">
    </div>
    <header class="">
        <ul class="navi">
            <li>LOGO</li>
        </ul>
    </header>
    <main></main>

    <script>
        const header = document.querySelector('header');
//instanceの生成-関数を登録
        const observer = new IntersectionObserver(toggleFixed);
//監視要素を登録
        observer.observe(document.querySelector('#mainvisual'));

        function toggleFixed(entries) {
            entries.forEach(e => {
//実質処理はここから
                if (e.isIntersecting) {
//default設定時は監視要素が
//画面にある時
                    header.classList.remove('fixed');
                } else {
//ない時
                    header.classList.add('fixed');
                }
            });
        }
    </script>
```
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="gOoxqbX" data-preview="true" data-editable="true" data-user="y0sh1m0t0" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/y0sh1m0t0/pen/gOoxqbX">
  Untitled</a> by y0sh1m0t0 (<a href="https://codepen.io/y0sh1m0t0">@y0sh1m0t0</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
# 設定できるオプション

```js:オプション
//下はdefaultの設定
    let options = {
        root: null,//viewport
        rootMargin: '0px',
        threshold: 0
    }
//instance時、第2引数に登録する
    let observer = new IntersectionObserver(callback, options);
```

- root
  - defaultはviewport
  - スクロールをもつ要素を指定することで対象を変更できる
    - ```js:
        root: document.querySelector('#main'),
      ```

- rootMargin
  - defaultはroot全体の'0% 0% 0% 0%';
  - 単位は0でも必須 %とpx が使える
  - 正の値だとrootの外側に拡大する
  - 負の値だとrootの内側に縮小する
  - 下の画像は`rootMargin:'-10% 0% -10% 0%',`の時を可視化したもの
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/613082a0-1f57-0019-59f8-c5c23d4a822f.png)
  - `rootMargin:'-50% 0%',`の時は領域は線になる
     - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/d48364f3-0c83-e05e-c2de-948cc566fdfe.png)
     - 下のコードペンは負の時のルートマージンの領域を可視化してみた
<p class="codepen" data-height="500" data-default-tab="result" data-slug-hash="wvpeOBv" data-editable="true" data-user="y0sh1m0t0" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/y0sh1m0t0/pen/wvpeOBv">
  Untitled</a> by y0sh1m0t0 (<a href="https://codepen.io/y0sh1m0t0">@y0sh1m0t0</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

- サンプル
- 画像の遅延読み込み
  - rootMargin:'0px 0px 300px',
  - 監視領域を下に300px拡大しているviewportに表示される前に画像を読み込んでいる
  - <p class="codepen" data-height="500" data-default-tab="js,result" data-slug-hash="KKZXrQz" data-editable="true" data-user="y0sh1m0t0" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">

 
- threshold: 0
  - defaultは `0`で、この場合は`intersectionRatio(交差量)`プロパティ(下に説明)の値を気にする必要がなく、監視要素がrootと交差した時に、`isIntersecting`は`true`になってcallback関数が実行される。また、交差が外れた時に`isIntersecting`は`false`になるので、この際にも、callback関数が実行される。
  - 下のサンプルは、`threshold`がdefaultの`0`の時、領域と交差した時に`isIntersecting`は`true`になりコールバック関数が実行され、交差が外れた時に`isIntersecting`は`false`になって再度コールバック関数が実行される動作を可視化してみました。

<p class="codepen" data-height="400" data-default-tab="result" data-slug-hash="RwxgmxJ" data-preview="true" data-editable="true" data-user="y0sh1m0t0" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/y0sh1m0t0/pen/RwxgmxJ">
  Untitled</a> by y0sh1m0t0 (<a href="https://codepen.io/y0sh1m0t0">@y0sh1m0t0</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
 
 - 上記で見た通り`threshold`が`0`の時は`isIntersecting`の判定は交差しているかどうかだけでしたが、50%ぐらい要素が交差した段階でコールバック関数を実行したい場合には、交差量を判定条件に入る必要があります。その際、`threshold`の値を変更します。交差量が50%になった時にコールバック関数を実行したいときは`threshold`の値を`0.5`に設定します。
 - `intersectionRatio`プロパティとは
   - 交差量の値を取得することができるプロパティで以下の注意点があります。 
   - 監視要素を100%として 監視要素と監視領域との交差割合が`intersectionRatio`プロパティの値になる。
   - 交差している値は0.0〜1.0で表示される。
   - root領域が100％ではない。
 
 - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/0911d549-08f2-96d0-f8cc-7e147beb1835.png)
- オプションの`threshold`の値を変更することで、`isIntersecting`の`true`と`false`の判定条件として交差量を付け加えることができる。
- `threshold: 1.0`だと`isIntersecting`の判定は交差しておりかつ、`intersectionRatio`が`100%`の時に`isIntersecting`は`true`になってコールバック関数が実行される。`intersectionRatio`が`100%`未満になった時に`isIntersecting`は`false`になって再度コールバック関数が実行される。
  - よって1.0で下記の図の場合、コールバック関数は実行されない。
  - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/671e712e-39a6-f699-2b26-14263dd4a660.png)
[intersectionRatioとrootとの交差割合の関係を可視化してみたけど余計意
味が分からなくなったデモサイト](http://abc18717397.html.xdomain.jp/sample.html)

:::note alert
下のコードペンは私が書いたものではありません
:::

- 下のコードペンは
   - オプションの設定は`threshold: 0.5`で`rootmargineが0の画面全体が領域になってます。
   - ということは`isIntersecting`がtrueになる条件は 交差 かつ `intersectionRatio(交差量)`が50%の時にtrueになり、コールバック関数が実行され、`intersectionRatio(交差量)`が50%未満になった時に`isIntersecting`は`false`になってコールバック関数が再度実行されている動作を可視化したものになっています。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="wvvGzQp" data-preview="true" data-editable="true" data-user="liquidlight" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/liquidlight/pen/wvvGzQp">
  Trigger animations with IntersectionObserver API</a> by Liquid Light (<a href="https://codepen.io/liquidlight">@liquidlight</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

- 複数の threshold: [0.5,1.0]
  - `threshold`は配列で 複数登録もできる。
  - 上の場合intersectionRatioが50%,100%の条件を<br>満たした毎にコールバック関数が実行される。
  - 上の場合、`isIntersecting`が`false`になる条件はintersectionRatioが50%未満になった時でその際にもコールバック関数が実行される。
    - 100%の条件を満たした後,100%以下になった時(100%以上はありえない)に、isIntersectingは'true'のままでコールバック関数は実行されません。再度100%になった時にはコールバック関数は実行され、50%以下になった時に isIntersectingは`false`になってコールバック関数が実行されます。
  - ```js:threshold作成の公式
        let options = {
            root: null,
            rootMargin: "0px",
            threshold: buildThresholdList()
        };     
        function buildThresholdList() {
            let thresholds = [];
     //numStepsを変更することで必要な数を追加できる。
     //監視要素を100分割したかったら100
            let numSteps = 20;

            for (let i = 1.0; i <= numSteps; i++) {
                let ratio = i / numSteps;
                thresholds.push(ratio);
            }

            thresholds.push(0);
            return thresholds;
        }
    ```
- 用途としては、要素の中をスクロールイベントのようにスクロール量に応じて変化させたい時に相応に分割して要素内でスクロールの上下に応じてイベントを発生させることができます。

:::note alert
下のコードペンは私が書いたものではありません
:::

- 下のコードペンでは監視要素を1/200分割して監視領域での交差量をコールバック関数で取得してその値でtextContent(data)を書き換えています。
- <p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="zaNPgd" data-editable="true" data-user="iondrimba" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/iondrimba/pen/zaNPgd">
  Intersection Observer API Demo</a> by Ion Drimba Filho (<a href="https://codepen.io/iondrimba">@iondrimba</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

その他
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="MWzaePQ" data-user="y0sh1m0t0" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/y0sh1m0t0/pen/MWzaePQ">
  Untitled</a> by y0sh1m0t0 (<a href="https://codepen.io/y0sh1m0t0">@y0sh1m0t0</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
 
- その他

- `unobserve`
   - 登録要素の監視を終了させる。
   - ```js:
       observer.unobserve(entry.target);
     ```

- `disconnect`
   - オブザーバーに変更の監視を停止させます。 
   - オプションやコールバック関数を変更したい時に<br>一旦停止して再度登録すると初期化できる。
   - 停止させずに登録すると前の<br>コールバック関数とオプションも残った状態になる。
   - ```js:       
       observer.disconnect();
     ```

- `'IntersectionObserver' in window`
   - IntersectionObserverが使用可能かどうか<br>判定してくる。 
   - ```js:
         if ('IntersectionObserver' in window) { 
                //処理
          }
     ```

- IE11への対策
 - IE11はIntersection Observerに対応していませんが、以下のpolyfillを１行読み込ませるだけで対応可能になります。
   -  ```html:
        <script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver"></script>
      ```  
# 無限スクロール
<p class="codepen" data-height="800" data-default-tab="result" data-slug-hash="yLRBGMR" data-user="y0sh1m0t0" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/y0sh1m0t0/pen/yLRBGMR">
  Untitled</a> by y0sh1m0t0 (<a href="https://codepen.io/y0sh1m0t0">@y0sh1m0t0</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>