【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記事書きます!以上。