angulareventsangular10queryselectorangular-renderer2

angular event listener document loaded


I am trying to select all pre elements in an angular component AFTER the document has been loaded.

ngAFterViewChecked, ngAfterContentChecked, ngAfterContentInit basically run 30 times before the document is loaded, which will slow down my app.

So, that leaves ngAfterViewInit, which logically one would think would work, but does not do anything because the document is not loaded yet. So, I am thinking an event listener as in vanilla js.

 constructor(
    @Inject(DOCUMENT) private document: Document,
    private renderer: Renderer2
 ) { }

 ngAfterViewInit(): void {

    this.renderer.listen('document', 'load', (event) => {
      console.log("loaded")
      const pres = this.document.querySelectorAll('pre');
      for (var i = 0; i < pres.length; ++i) {
        console.log("Yes!");
      }
    });

  }

This does not work as expected, but I have not seen any other methods quite try something this way, so I am thinking something similar could work.

Note: My pre tags are loaded asynchronously from a database in an [innerHTML]="content".

Any ideas?


Solution

  • You can use RendererFactory2 to do this. This lets you attach a callback that gets called when the rendering is done.

    constructor(private rendererFactory: RendererFactory2) { }
    
      ngOnInit() {
        this.rendererFactory.end = () => {
          const pres = this.document.querySelectorAll('pre');
          for (var i = 0; i < pres.length; ++i) {
            console.log("Yes!");
          }
        }
      }
    
    

    A note of caution, the RendererFactory2 that is injected is a singleton so you may have to tweak this code and set the 'end' callback to null when you are done with it or it will keep getting called.

    Update

    After posting this I realized that there is a better and more proper way of doing this. You can use a MutationObserver to listen for changes to the DOM - like setting the innerHTML on a DOM element.

    Here is the MDN documentation

    https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

    Fortunately the Angular CDK has a directive called cdkObserveContent that implements MutationObserver. Documentation is here:

    https://material.angular.io/cdk/observers/overview

    You need to install the Angular CDK if you don't have it installed already and import the ObserversModule and then you can add the cdkObserveContent listener to the DOM element that you are inserting the html into. With this approach the listener will only be triggered once when the html is inserted so you don't have to worry about multiple calls.