JavaScriptに関するお知らせ

SINCE2019
>
【JS】IntersectionObserverで複数を監視

【JS】IntersectionObserverで複数を監視


こんにちは!JavaScriptのnewの意味が4年経ってもあんま分からないMizutani(@sirycity)です。オブジェクト指向に触れ合う機会がなかったの...

今日はIntersectionObserverで複数を監視する方法についてです。あとついでに簡単な書き方も紹介する。

結論

短く書いたバージョン

なるべく短く書くとこんな感じ。

window.addEventListener('DOMContentLoaded', () =>
  ['foo', 'bar', 'baz'].map((id, i) =>
    new IntersectionObserver(([e]) =>
      e.isIntersecting
        ? console.info(`${i}番目のやつが画面に入りました`)
        : console.info(`${i}番目のやつが画面から出ました`),
    ).observe(document.getElementById(id)),
  ),
)

丁寧に書いたバージョン

こんな感じ。上のやつと挙動は一緒よ。分解しただけ。

const ids = ['foo', 'bar', 'baz']

const elements = ids.map(({ id }) => document.getElementById(id))

const checkIntersection =
  i =>
  ([e]) =>
    e.isIntersecting
      ? console.info(`${i}番目のやつが画面に入りました`)
      : console.info(`${i}番目のやつが画面から出ました`)

const registarIntersection = (element, i) =>
  new IntersectionObserver(checkIntersection(i)).observe(element)

const registarIntersections = () => elements.map(registarIntersection)

window.addEventListener('DOMContentLoaded', registerIntersections)

解説

↑の丁寧に書いたバージョンを6箇所に分けて解説してきます。こんな感じに

// 1
const ids = ['foo', 'bar', 'baz']

// 2
const elements = ids.map(({ id }) => document.getElementById(id))

// 6
const checkIntersection =
  i =>
  ([e]) =>
    e.isIntersecting
      ? console.info(`${i}番目のやつが画面に入りました`)
      : console.info(`${i}番目のやつが画面から出ました`)

// 5
const registarIntersection = (element, i) =>
  new IntersectionObserver(checkIntersection(i)).observe(element)

// 4
const registarIntersections = () => elements.map(registarIntersection)

// 3
window.addEventListener('DOMContentLoaded', registerIntersections)

1. 使うidを羅列

画面内に入ってきたかを判定したい要素がいくつかあるとします。こんな感じに

<div>こいつが画面内に入ってきたか判定したい</div>
<div>こいつも判定したい</div>
<div>こいつも</div>

まずはこの3つにidを付けてまいましょう。 ここは色々やり方あるけど(同じクラスにするとか)、今回はバラバラのidにする。こう

<div id="foo">こいつが画面内に入ってきたか判定したい</div>
<div id="bar">こいつも判定したい</div>
<div id="baz">こいつも</div>

ok、そしたらJS側に今付けたidを配列で定義しましょう。こう

const ids = ['foo', 'bar', 'baz']

これでおわり。

2. DOMを取得

1.で定義したIDのDOMを取得します。こう。

const elements = ids.map(({ id }) => document.getElementById(id))

わけ分からない?要はやってることはこれと一緒よ。

const elements = [
  document.getElementById('foo'),
  document.getElementById('bar'),
  document.getElementById('baz'),
]

こんだけのこと。「ID」を「そのIDが付いている要素」に変換するだけね!

「アロー関数」とか「分割代入」とか「javascript map」でググるともっと詳しいのが出ると思う。

3. ページが読み込み終わったら発火する処理

こんだけ。

window.addEventListener('DOMContentLoaded', registerIntersections)

これはコピペ感覚でいいかな?ページが読み込み終わったらregisterIntersectionsを実行するよーってだけ。

4. 全ての要素にintersectionを登録する準備

ここは監視対象一つなら不要。今回は監視対象(elements)が複数なのでループしてる。そんだけ。

const registarIntersections = () => elements.map(registarIntersection)

5. IntersectionObserverを監視対象を指定して発動

IntersectionObserver発動。監視対象にelement(idを元に取ってきたDOM)を指定してます。

「このelementってやつを監視せえよ」ってイメージ。

具体的な監視内容は中の6.checkIntersectionで定義します

const registarIntersection = (element, i) =>
  new IntersectionObserver(checkIntersection(i)).observe(element)

6. 監視の処理を定義

今回は短いけどこんな感じ。

const checkIntersection =
  i =>
  ([e]) =>
    e.isIntersecting
      ? console.info(`${i}番目のやつが画面に入りました`)
      : console.info(`${i}番目のやつが画面から出ました`)

いくつかポイント解説。

([e])ってやつは引数()の配列の0番目の要素です。普通は(foo)みたいな感じで取って、foo[0]で最初の要素にアクセスする...みたいな書き方するね。

今回は引数に来るのがidに紐付いたDOMで、かつidは1つしか付けられないので配列の長さが1であることが確定しているからこんな書き方してます。

配列の長さが決まってたり、配列のn番目しか使わなかったりする場合に使うといいかも。ちなみに使わないとこんな書き方になる

const checkIntersection = i => e =>
  e[0].isIntersecting
    ? console.info(`${i}番目のやつが画面に入りました`)
    : console.info(`${i}番目のやつが画面から出ました`)

ちなみにちょっと前に書いたけどclassでDOM取る方法だとここの配列(e)が長さ複数になってめんどいです。今回はidで取ってるから長さは絶対1。安心。

そんで、その後のisIntersectingってのが画面内か(true)画面外か(false)の判定です。今回は画面内外に分けてconsole.logしてるけど、ここには任意の処理を書くといいと思う。

結論

うん、仕様が悪いな

なんかもうちょいわかりやすくできなかったのかな(ECMAに喧嘩を売っていくスタイル) intersectionは普通にaddEventLintenerに付けてほしかった。 newとか言われてもね。わかんないね。

intersection関連でもう1記事書きます!以上。



PREV
2020-05-18
【CSS】ReactやVueのCSS設計でBEMがイマイチな理由

NEXT
2020-05-29
【JS】日本でVueがReactより人気な理由を考える