ちゃんと理解していないのに使ってしまいました...Interseprtion Observer。
このまま年を越すわけにはいかないと思ったので、2020年の力試しとしてまとめます。
2020年最後の記事です。
Interseprtion Observerとは
とてもわかりやすく、詳しく書かれているので、こちらを見て分かる方は十分です。
JSでのスクロール連動エフェクトにはIntersection Observerが便利 - ICS MEDIA
サンプルコードと動きの概要
上記記事から引用させていただきます。
文字通り「解読」なのでこの詳しい記事をさらに細かく見ていきます!
// 今回の交差を監視する要素 const boxes = document.querySelectorAll(".box"); const options = { root: null, // 今回はビューポートをルート要素とする rootMargin: "-50% 0px", // ビューポートの中心を判定基準にする threshold: 0 // 閾値は0 }; const observer = new IntersectionObserver(doWhenIntersect, options); // それぞれのboxを監視する boxes.forEach(box => { observer.observe(box); }); /** * 交差したときに呼び出す関数 * @param entries */ function doWhenIntersect(entries) { // 交差検知をしたもののなかで、isIntersectingがtrueのDOMを色を変える関数に渡す entries.forEach(entry => { if (entry.isIntersecting) { activateIndex(entry.target); } }); } /** * 目次の色を変える関数 * @param element */ function activateIndex(element) { // すでにアクティブになっている目次を選択 const currentActiveIndex = document.querySelector("#indexList .active"); // すでにアクティブになっているものが0個の時(=null)以外は、activeクラスを除去 if (currentActiveIndex !== null) { currentActiveIndex.classList.remove("active"); } // 引数で渡されたDOMが飛び先のaタグを選択し、activeクラスを付与 const newActiveIndex = document.querySelector(`a[href='#${element.id}']`); newActiveIndex.classList.add("active"); }
これはスクロールに合わせて目次の見出しに背景色がつくといったものです。
簡単にHTML+CSSの様子を表すと、こうなります。
白い部分がビューポート(ディスプレイ)です。
#indexList
というところが目次です。
.box
を監視して、ビューポートに表示されるとその .box
に紐付いた目次の見出しに.active
のクラスが付きます。
解読
まとまりごとに見ていきます。
// 今回の交差を監視する要素 const boxes = document.querySelectorAll(".box"); const options = { root: null, // 今回はビューポートをルート要素とする rootMargin: "-50% 0px", // ビューポートの中心を判定基準にする threshold: 0 // 閾値は0 };
監視要素の.box
をboxes
に入れる。
querySelectorAll
メソッドは指定したセレクタのHTML要素(NodeList)を取得します。
boxesを console.log(boxes)
とすると、
NodeList(4) [div.box, div.box, div.box, div.box]
などと出てきます。
そして options
というオブジェクトを作る。
{}
で囲まれているのでオブジェクトです。
ここまでは大丈夫です。
次のまとまりを見ます。
const observer = new IntersectionObserver(doWhenIntersect, options);
new
でこれも IntersectionObserverのオブジェクト、observer
というインスタンスを作っています。
ここでIntersectionObserverのオブジェクトって何?となりました。
IntersectionObserverはコンストラクター
調べていくと、「IntersectionObserverはコンストラクター」だそうです。
コンストラクター(関数)
new式を使用して新規オブジェクト(インスタンス)を作成する関数。
そういう関数があらかじめ用意されているようです。
これがいわゆる「API」ということでしょうか!
クライアントサイドJavaScriptとはブラウザで動くJavaScriptのことですね。
そのうちのAPIは以下の2つに分けられるとあります。
- ブラウザ API
┗ ブラウザに組み込まれているもの。 ←IntersectionObserverはこっち? - サードパーティ API
┗ デフォルトではブラウザに組込まれていない。Twitter APIなど
Web APIとしてこちらにもIntersectionObserverが載っていました。
Web API は通常 JavaScript とともに使用されますが、常にそうとは限りません。
意味深ですね。他の言語でもWeb API が使われることがあるみたいです。
IntersectionObserver
IntersectionObserver - Web API | MDN
Intersection Observer API - Web API | MDN
苦手意識のあるAPI、また少し分かってきた気がします。
ひとくくりにするにはその種類が多いですね。
IntersectionObserver()
の中身を見てみると、doWhenIntersect
がコールバック関数で、options
が監視範囲の設定値です。
コールバック関数の中身はそのさらに下に書かれているので今は置いておいて...。
// それぞれのboxを監視する boxes.forEach(box => { observer.observe(box); });
監視対象の .box
要素をループ処理しています。
ここでforEachについて調べました。
JavaScriptでforEachメソッドを使う方法【初心者向け】 | TechAcademyマガジン
forEachはArrayオブジェクトに実装されたメソッド
配列もオブジェクトなのでメソッドがあります。書き方は下記です。
配列.forEach( コールバック関数による処理 )
ここでもコールバック関数!
box => { observer.observe(box); }
この部分、アロー関数で書かれていますが、
function(box) { observer.observe(box); }
と同じです。box
は.box
の各要素です。
observeメソッド
このはたらきは
IntersectionObserver が対象の要素を監視するよう命じます。
だそうです。
observer.observe(box);
は、observerというオブジェクトに対してobserveメソッドをあてています。名前が一緒でややこしいですね。
引数に対象の要素を入れています。
つまりは「IntersectionObserver が各 .box
要素を監視する」ということですね。
/** * 交差したときに呼び出す関数 * @param entries */ function doWhenIntersect(entries) { // 交差検知をしたもののなかで、isIntersectingがtrueのDOMを色を変える関数に渡す entries.forEach(entry => { if (entry.isIntersecting) { activateIndex(entry.target); } }); }
ここで IntersectionObserver()
のコールバック関数が出てきました。
そしてまたforEach。
なぜforEachを使っているのかは、参考本ページに書かれていました。
doWhenIntersect(entries)
と引数を設けています。
ここには、doWhenIntersect
が発動するタイミングで、監視している要素が入ってくるのだと思います。
コールバックは、入力引数として交差したすべての閾値を示すIntersectionObserverEntry オブジェクトの配列を、また参照としてIntersectionObserver オブジェクト自身を受け取ります。
ちょっと違いました^^;
IntersectionObserverEntry オブジェクトがよく分かりません。
閾値のリスト内のそれぞれの項目は、通過した閾値を説明するIntersectionObserverEntry オブジェクトです。つまり、それぞれの項目は指定された要素がルート要素とどれだけ交差したのか、要素が交差したと言えるのかどうか、推移が発生した方向を示します。
console.log(entries)
とすると、閾値に指定している(今回はthreshold: 0)、交差のタイミングで表示されました。
分かったような気がします。大事なオブジェクト...!
isIntersectingプロパティ
監視対象が見えるようになったか、見えなくなったかを判断します。
entry.isIntersecting
が true
なら見えるようになった
false
なら見えなくなった
です。
(見えている状態が継続されている場合はtrueなのでしょうか)
要素が見え始めた場合に activateIndex
という関数に entry.target
という引数を渡しています。
targetプロパティ
先程の console.log(entries)
のtargetプロパティを見てみると div.box
とありました。
このあたりの細かい説明はあまり見つからず...「targetプロパティ」は広くいろいろなところで同じようにセレクタを値に持っているのかもしれませんね。
文章にすると「閾値の交差時に監視対象の要素が見えたら、次の関数を実行する」でしょうか。
/** * 目次の色を変える関数 * @param element */ function activateIndex(element) { // すでにアクティブになっている目次を選択 const currentActiveIndex = document.querySelector("#indexList .active"); // すでにアクティブになっているものが0個の時(=null)以外は、activeクラスを除去 if (currentActiveIndex !== null) { currentActiveIndex.classList.remove("active"); } // 引数で渡されたDOMが飛び先のaタグを選択し、activeクラスを付与 const newActiveIndex = document.querySelector(`a[href='#${element.id}']`); newActiveIndex.classList.add("active"); }
これは読めました。
目次に対して、先にactiveクラスを外してから、該当の要素にactiveクラスを付けています。
以上です!
Lozad.jsについて
こちらのプラグインの中でInterseprtion Observerが使われているということなので調べました。
IntersectionObserveの遅延読み込みライブラリのLozad.jsを紹介する - Qiita
使いやすそうですね。Interseprtion Observer を理解した上で使うならいいと思いました。
最後に
JavaScriptはまだまだ1文ずつ分解して読んでいかないと分からないところがあります。
謎のプロパティ、引数が出てくると心が折れそうになりますが、調べれば正体が分かったので、根気よく解読していこうと思います。
アロー関数とコールバック関数に慣れなくては。
***
2020年に始めたこのブログも何とか1年続けられました。奇跡!
後半息切れしましたが、2021年もマイペースに書いていきます!