0

Currently I have an Angular component that appends a list of script tags into the DOM with Renderer2 library, the code of these scripts is retrieved from an external source. In the following snippet, scripts array is the list of source links. Being this the case, I'm unable to modify the JS code from them:

  for(let i = 0; i<scripts.length; i++){
    let script = this.renderer2.createElement('script');
    script.type = `text/javascript`;
    script.src = scripts[i];
    this.appendedChildren.push(script);
    this.renderer2.appendChild(this._document.body, script);
}

I tried to remove them from the DOM, but the scripts keep executing:

ngOnDestroy(){
  for(let i = 0; i<this.appendedChildren.length; i++)
    this.renderer2.removeChild(this._document.body, this.appendedChildren[i]);
}

What I would like is to get the pid or some kind of identificator to be able to kill the JS scripts within the ngOnDestroy(), and also a method to do it so.

Across
  • 63
  • 5
  • The pid is your browser's tab, since Javascript is single-threaded. There is not one new process spawned for each ` – Jeremy Thille Dec 27 '21 at 12:56
  • Currently I get the source links from an external API, these are scripts that render ads. I need to stop showing these ads when the user triggers some condition, such as pressing a button. It's hard for me to see a different approach than this, what I could do is to get the JS code from the src and modify it dynamically (adding some common method) to kill it when something is done from the component, but that looks tricky and hard indeed. – Across Dec 27 '21 at 13:15
  • If you need to hide the ads, then simply use CSS `display:none`? – Jeremy Thille Dec 27 '21 at 13:18
  • I've been reading the scripts I get, the thing is that there is no common ground between them, each one generates an img or an iframe tag without adding any class or id, and they append it into the body, which makes them impossible to detect and therefore to hide them. I'll ask the API dev to add a class inside the elements that the scripts generate, which I can parse afterwards and hide the ads, I don't see any other feasible solution. Thanks for your help, will update this thread as soon as I get news. – Across Dec 27 '21 at 13:58
  • Ooof, sounds like some headache :/ Good luck with this, then – Jeremy Thille Dec 27 '21 at 14:25

2 Answers2

0
constructor(private renderer: Renderer2) {}

let script = this.renderer.createElement('script');
script.type = `text/javascript`;
script.src = 'testme.js';
script.id = "testScriptName";
this.renderer.appendChild(this._document.body, script);

ngOnDestroy() {
var elem = document.querySelector("#testScriptName");
document.querySelector("#testScriptName").parentNode.removeChild(elem)
}
0

Okay, I've been told a way to do it and it works flawlessly.

Instead of creating scripts, I can create an iframe and pass all the scripts using the attribute srcdoc from the iframe, this would be the structure I want to create from the Angular component and append to the DOM, I should be creating an iframe per script:

  <iframe class="iframe-container" srcdoc='
  <html>
    <head></head>
    <body>
      <script src="https://script-source.com"></script>
    </body>
  </html>'>
</iframe>

So to create and append the scripts from the Angular component, notice that I'm adding the component into an appended children array (I'm aware there are cleaner ways to do this, just to get the idea):

let iframe = this.renderer2.createElement('iframe');
iframe.setAttribute('class', 'iframe-ad');
iframe.setAttribute('srcdoc', `
  <html>
    <head></head>
    <body>
      <script src="https://src-example-url.com"></script>
    </body>
  </html>
`);

this.appendedChildren.push(iframe);
this.renderer2.appendChild(this._document.body.querySelector('div.iframe-holder'), iframe);

In order to delete the iframe, just adding these lines into the ngOnDestroy() makes it:

ngOnDestroy(){
  for(let i = 0; i<this.appendedChildren.length; i++){
    this.renderer2.removeChild(this._document.body, this.appendedChildren[i])
  }
}
Across
  • 63
  • 5