scrollイベントを用いずに「topから〇〇pxスクロールしたらどうのこうの」する

## やりたきこと
scrollイベントを用いることなく、「topから〇〇pxスクロールした」瞬間を検知したい。

## 結論
`IntersectionObserver`で`document.body`を監視し、`observer`の`rootMargin.top`に境界となる値を設定する。

```ts:Typescript
const border = 100; // 100pxスクロールしたらどうのこうのする

const option = {
    root: null,
    // rootMargin.topに境界を、rootMargin.bottomにbodyの高さを設定
    rooMargin: `${border}px 0px ${document.body.clientHeight}px 0px`,
    threshold: 1
};

const callback = (entries: IntersectionObserverEntry[]) => {
    // IntersectionObserverEntryはundefinedの可能性があるので型ガード
    if (typeof entries[0] === 'undefined') return;

    if (!entries[0].isIntersecting) {
        // isIntersecting === false のとき、100pxスクロールした瞬間
        doNanyaKanya(); // なんやかんや
    }
};

const observe = new IntersectionObserver(callback, option);
observe.observe(document.body); // body全体を監視
```

## `IntersectionObserver `とは
`IntersectionObserver`APIは、あるターゲット要素を監視し、ビューポートまたは特定の親要素から出たり入ったりした時、あるいは両者が交差する量が要求された量だけ変化した時に、登録されたコールバック関数を実行するというAPIです。

[交差オブザーバー API - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API)

### 基本的な`IntersectionObserver`の使い方
基本的にはある要素をターゲットとして監視し、画面（viewport）に入ったらどうの・出たらこうのする、という使い方が基本かと思います。
スクロールしていくとふわっ…と現れる要素なんかによく使われます。

<p class="codepen" data-height="500" data-default-tab="html,result" data-slug-hash="MWrzYoQ" data-user="MentTori" 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/MentTori/pen/MWrzYoQ">
  IntersectionObserver Demo</a> by MenTori (<a href="https://codepen.io/MentTori">@MentTori</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>



## 本題: `IntersectionObserver`で「〇〇pxスクロールしたかどうか」を監視する
### 「スクロール量に応じて」という実装はできない
特定の要素を監視してviewportに入ったか・出ていったかを判定して関数を実行するAPIなので、スクロールしたら関数を実行、ということはできないです。
ターゲット要素をtopから100pxの位置に配置すればもちろん可能ですが、そういうことじゃない….


### `rootMargin`でviewportを上部に拡張して、topにある要素を監視する
`IntersectionObserver`APIにはオプションを色々設定できるのですが、その中に`rootMargin`という設定項目があります。こちらは**viewportの拡大・縮小をさせることができる**設定で、cssの`margin`プロパティのような値を指定します。
`rootMargin: 100px 0px 0px 0px;`と指定するとviewportが上部に100px拡張され、画面上端から100px上部に境界を設定できます。

この設定により、**`rootMargin: 100px 0px 0px 0px;`でviewportを拡張してヘッダーなどのページ最上部に位置する要素を監視すれば、下方向にスクロールしてヘッダーが画面上端から100pxの位置に来た瞬間をオブザーバーで検知**することができます！
これはつまり「100pxスクロールした瞬間」に他ならないのです！！やったぜ解決！！👏


### より汎用的に`body`を監視する
上記の実装で100pxスクロールした瞬間の検知はできました。しかしページ最上部に必ず要素があるとは限りません。そこで、絶対に画面上部に接しているDOM要素を監視するようにすればいいのではと考えました。
**そう、body要素です**。

しかし、bodyは大体画面（viewport）より高さがあるため常に`isIntersecting === false`となってしまっていることが問題になります。
上記の100pxスクロール検知は境界を画面上端より100pxにまで拡張してそのviewportから要素が少しでも出た瞬間を検知しているのですが、**bodyは大きすぎて常にviewportから出てしまっている**ため検知ができないのです。

そこで再び`rootMargin`を用いて、今度は下方向にviewportを拡大します。どれくらい拡大するかというと**bodyの高さ分だけ拡大**します。
こうすることによりbodyがviewportにすっぽり入るため、スクロールしていない時点では`isIntersecting === true`となり、100pxスクロールしてbodyの上端が境界から少しでも出た瞬間に`isIntersecting === false`となります。
これで汎用的に100pxスクロールした瞬間を検知することができました！！💪
（なんだかめっちゃ裏技じみている気がしますが…）

以下が完成形のコードです。
```ts
const border = 100; // 100pxスクロールしたらどうのこうのする

const option = {
    root: null,
    // `rootMargin.top`に境界を、rootMargin.bottomにbodyの高さを設定
    rooMargin: `${border}px 0px ${document.body.clientHeight}px 0px`,
    threshold: 1
};

const callback = (entries: IntersectionObserverEntry[]) => {
    // IntersectionObserverEntryはundefinedの可能性があるので型ガード
    if (typeof entries[0] === 'undefined') return;

    if (!entries[0].isIntersecting) {
        // isIntersecting === false のとき、100pxスクロールした
        doNanyaKanya(); // なんやかんや
    }
};

const observe = new IntersectionObserver(callback, option);
observe.observe(document.body); // body全体を監視
```


ちなみに画面下部から`document.body.clientHeight`分viewportを拡張しているため、画面の高さ`window.height`分だけ余分に広げてしまっています。別に問題ないとは思いますが、気になる方は`${document.body.clientHeight - window.height + 1}px`みたいにギリギリを攻めるといいと思います。✍🏻


## さいごに
もちろんscrollイベントを使えば簡単なことはわかっております、一応。使いたくないから頑張ったんです、一応。
完全に独自実装ですのでおかしな点多々あるかと思いますし、「いやそもそもそんなややこしいことしなくてもコレでできるが？？」ってのがありましたら是非に教えていただきたいです...👀