この記事では、要素の可視性の状態、つまり要素がいつ出入りするかを追跡する非常に単純な Angular ディレクティブを作成する方法を説明します。ビューポート。これが素晴らしい、そしておそらく役立つ演習になることを願っています!
これを行うには、最新のブラウザで利用できる IntersectionObserver JavaScript API を使用します。
ディレクティブを次のように使用したいと思います:
<p visibility [visibilityMonitor]="true" (visibilityChange)="onVisibilityChange($event)" > I'm being observed! Can you see me yet? </p>
出力は次の形式になります:
type VisibilityChange = | { isVisible: true; target: HTMLElement; } | { isVisible: false; target: HTMLElement | undefined; };
ターゲットが未定義であるということは、要素が (@if などによって) DOM から削除されたことを意味します。
私たちのディレクティブは単に要素を監視するだけであり、DOM 構造は変更しません。これは 属性ディレクティブ になります。
@Directive({ selector: "[visibility]", standalone: true }) export class VisibilityDirective implements OnInit, OnChanges, AfterViewInit, OnDestroy { private element = inject(ElementRef); /** * Emits after the view is initialized. */ private afterViewInit$ = new Subject<void>(); /** * The IntersectionObserver for this element. */ private observer: IntersectionObserver | undefined; /** * Last known visibility for this element. * Initially, we don't know. */ private isVisible: boolean = undefined; /** * If false, once the element becomes visible there will be one emission and then nothing. * If true, the directive continuously listens to the element and emits whenever it becomes visible or not visible. */ visibilityMonitor = input(false); /** * Notifies the listener when the element has become visible. * If "visibilityMonitor" is true, it continuously notifies the listener when the element goes in/out of view. */ visibilityChange = output<VisibilityChange>(); }
上記のコードには次のように表示されます:
そして当然のことながら、ディレクティブを適用する DOM 要素を取得するために ElementRef を挿入します。
main メソッドを記述する前に、ディレクティブのライフサイクルに注意しましょう。
ngOnInit(): void { this.reconnectObserver(); } ngOnChanges(): void { this.reconnectObserver(); } ngAfterViewInit(): void { this.afterViewInit$.next(); } ngOnDestroy(): void { // Disconnect and if visibilityMonitor is true, notify the listener this.disconnectObserver(); if (this.visibilityMonitor) { this.visibilityChange.emit({ isVisible: false, target: undefined }); } } private reconnectObserver(): void {} private disconnectObserver(): void {}
これで何が起こります:
これが私たちの指令の核心です。 reconnectObserver メソッドが観察を開始するメソッドになります。次のようになります:
private reconnectObserver(): void { // Disconnect an existing observer this.disconnectObserver(); // Sets up a new observer this.observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { const { isIntersecting: isVisible, target } = entry; const hasChangedVisibility = isVisible !== this.isVisible; const shouldEmit = isVisible || (!isVisible && this.visibilityMonitor); if (hasChangedVisibility && shouldEmit) { this.visibilityChange.emit({ isVisible, target: target as HTMLElement }); this.isVisible = isVisible; } // If visilibilyMonitor is false, once the element is visible we stop. if (isVisible && !this.visibilityMonitor) { observer.disconnect(); } }); }); // Start observing once the view is initialized this.afterViewInit$.subscribe(() => { this.observer?.observe(this.element.nativeElement); }); }
信じてください、思っているほど複雑ではありません!そのメカニズムは次のとおりです:
最後に、オブザーバーを切断するメソッドを簡単に実装しましょう:
private disconnectObserver(): void { if (this.observer) { this.observer.disconnect(); this.observer = undefined; } }
これが完全なディレクティブです。これは単なる練習問題なので、好きなものに自由に変更してください。
type VisibilityChange = | { isVisible: true; target: HTMLElement; } | { isVisible: false; target: HTMLElement | undefined; }; @Directive({ selector: "[visibility]", standalone: true }) export class VisibilityDirective implements OnChanges, OnInit, AfterViewInit, OnDestroy { private element = inject(ElementRef); /** * Emits after the view is initialized. */ private afterViewInit$ = new Subject(); /** * The IntersectionObserver for this element. */ private observer: IntersectionObserver | undefined; /** * Last known visibility for this element. * Initially, we don't know. */ private isVisible: boolean = undefined; /** * If false, once the element becomes visible there will be one emission and then nothing. * If true, the directive continuously listens to the element and emits whenever it becomes visible or not visible. */ visibilityMonitor = input(false); /** * Notifies the listener when the element has become visible. * If "visibilityMonitor" is true, it continuously notifies the listener when the element goes in/out of view. */ visibilityChange = output (); ngOnInit(): void { this.reconnectObserver(); } ngOnChanges(): void { this.reconnectObserver(); } ngAfterViewInit(): void { this.afterViewInit$.next(true); } ngOnDestroy(): void { // Disconnect and if visibilityMonitor is true, notify the listener this.disconnectObserver(); if (this.visibilityMonitor) { this.visibilityChange.emit({ isVisible: false, target: undefined }); } } private reconnectObserver(): void { // Disconnect an existing observer this.disconnectObserver(); // Sets up a new observer this.observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { const { isIntersecting: isVisible, target } = entry; const hasChangedVisibility = isVisible !== this.isVisible; const shouldEmit = isVisible || (!isVisible && this.visibilityMonitor); if (hasChangedVisibility && shouldEmit) { this.visibilityChange.emit({ isVisible, target: target as HTMLElement }); this.isVisible = isVisible; } // If visilibilyMonitor is false, once the element is visible we stop. if (isVisible && !this.visibilityMonitor) { observer.disconnect(); } }); }); // Start observing once the view is initialized this.afterViewInit$.subscribe(() => { this.observer?.observe(this.element.nativeElement); }); } private disconnectObserver(): void { if (this.observer) { this.observer.disconnect(); this.observer = undefined; } } }
以上がAngular LAB: 可視性ディレクティブを作成しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。