Intersection Observer が思うように発火しない Threshold オプションの勘違いについて

## はじめに

[Intersection Observer](https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API)、使ってますか？
スクロールでのイベント制御のように不要なイベントが多発しないですし、Intersection Observer という名前にも格好良さがあります。
また基本的なブラウザ（IE は除く）にはライブラリなどを読み込ませる必要もなく、その上名前も格好いいですし、なんといってもやはり名前が格好いい。
**「Intersection Observer で実装しました。」**っていう響きが、なんかとってもそれっぽくて魅力的ですよね。

## よくある使い方

**「要素が ○ 割程度画面に入ってきたら、クラス名を付与するとかしてアニメーションを発火させよう。」**
よくある使い方だと思います。自分は今までほとんどその使い方しかやった覚えがないです。

そんな Intersection Observer ですが、先日扱っていた時に思ったように動いてくれない場面がありました。

## 「ページ内の記事コンテンツが 8 割くらい読まれたらインプレッション飛ばして」

「あーなるほど。じゃあ Threshold を 0.8 に設定して、記事の 8 割部分が画面に入ってきたところでインプレッション飛ばしましょうか。。」

という発想で実装していきました。

<p class="codepen" data-height="400" data-theme-id="dark" data-default-tab="html,result" data-user="shineda" data-slug-hash="WNxMqjV" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="IntersectionObserver is not working">
  <span>See the Pen <a href="https://codepen.io/shineda/pen/WNxMqjV">
  IntersectionObserver is not working</a> by S.ieda (<a href="https://codepen.io/shineda">@shineda</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>


が、発火しませんでした。
（サンプルではインプレッションの代わりに文字色をオレンジに変更するよう実装しています。発火しませんが。）

## Intersection Observer の Threshold オプションについて勘違いしていたこと

勘違いとは「Threshold を 0.8 に設定しておけば **Intersection Observer が要素の 8 割の位置を監視**し、画面と交差したタイミングでインプレッションが発火する」と思っていたことです。

試しに [MDN ページのサンプル](https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API#Thresholds) を検証ツールで 600px まで伸ばし、確認してみました。

![画面収録-2020-10-29-22.46.39.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390806/e89fb3d3-1c5a-0717-d86b-2fc57bf30fc3.gif)

自分の（勘違いしていた）認識では、要素を下までスクロールしたところで 100% が表示されているイメージでした。しかし実際に画面に表示されているのは 81% であり、この割合が Threshold を超えなかったので発火しなかったわけですね。
Intersection Observer が監視しているのは、あくまで「**ターゲット要素とそのルート要素の交差する度合い（交差率）**」であり、要素が画面よりも長い場合、要素が画面内にどれくらい表示されているかという比率で数値が打ち止めとなるようです。

## 解決方法案

アンカー用のタグを要素とは別に、絶対配置で親要素の 8 割位置に配置することで解決しました。
だいたい以下のようなコードで実装しています。
（サンプルではインプレッションの代わりに文字色をオレンジに変更するよう実装しています。発火が確認できない場合は直接 CodePen のページでご確認ください。）

<p class="codepen" data-height="400" data-theme-id="dark" data-default-tab="html,result" data-user="shineda" data-slug-hash="bGeebeZ" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="IntersectionObserver is working">
  <span>See the Pen <a href="https://codepen.io/shineda/pen/bGeebeZ">
  IntersectionObserver is working</a> by S.ieda (<a href="https://codepen.io/shineda">@shineda</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## 参考

- [Intersection Observer API](https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API)
- [JSでのスクロール連動エフェクトにはIntersection Observerが便利](https://ics.media/entry/190902/)
- [Determine how much of the viewport is covered by element (IntersectionObserver)](https://stackoverflow.com/questions/57786082/determine-how-much-of-the-viewport-is-covered-by-element-intersectionobserver)