スクロールイベントを検知するAPIのIntersection Observerですが、インスタンス生成後にscrollMarginを動的に変更することはできません。
以下の記事でも、同じように悩んでいる人がいました。
"How to dynamically change Intersection Observer's configuration"
https://stackoverflow.com/questions/49511503/how-to-dynamically-change-intersection-observers-configuration
今回は、以下のようにラッパークラスを作り対応しました。
/**
* IntersectionObserverを拡張し、rootMarginの動的な更新をサポートするラッパークラス。
* 標準のIntersectionObserverでは、rootMarginを変更するためには新しいインスタンスの作成が必要だが、
* このクラスを使用することで、既存のインスタンスでrootMarginを容易に更新できる。
*/
class IntersectionObserverWrapper {
private _intersectionObserver: IntersectionObserver;
private _callback: IntersectionObserverCallback;
private _options?: IntersectionObserverInit;
private _targets: Element[] = [];
/**
* IntersectionObserverWrapperの新しいインスタンスを作成する。
* @param callback 要素の視認性の変化を通知するコールバック関数。
* @param options IntersectionObserverのオプション、rootMarginの初期値を含む。
*/
constructor(callback: IntersectionObserverCallback, options?: IntersectionObserverInit) {
this._intersectionObserver = new IntersectionObserver(callback, options);
this._callback = callback;
this._options = options;
}
/**
* rootMarginを更新する。このメソッドを呼び出すと、監視中の全ての要素に対して、
* 新しいrootMarginが適用される。
*
* @param rootMargin 新しいrootMarginの値(例: `-80px 0px -1000px 0px`)。
*/
updateRootMargin(rootMargin: string): void {
// 指定された値が現状と変わらなければ何もしない
if (this._intersectionObserver.rootMargin === rootMargin) {
return;
}
this._intersectionObserver.disconnect();
const newOptions = { ...this._options, rootMargin: rootMargin };
this._intersectionObserver = new IntersectionObserver(this._callback, newOptions);
this._targets.forEach(target => this._intersectionObserver.observe(target));
}
/**
* 指定した要素を監視対象に追加する。
*
* @param target 監視対象に追加する要素。
*/
observe(target: Element): void {
this._intersectionObserver.observe(target);
this._targets.push(target);
}
/**
* 監視を終了し、全ての要素の監視を停止する。
*/
disconnect(): void {
this._intersectionObserver.disconnect();
}
}