MWR
MWR

Reputation: 404

Execute script sourced to an external file loaded from AJAX

This is a very common question in here related to AJAX, but I can't seem to find one for my case, because most of them used jQuery, and other reasons. Probably mine isn't efficient/recommended, but either way, here we go.

I have a button [let's say we have a reference to it for the sake of it called btn].

It listens for a clicking event. When the user clicks the button, it makes an AJAX request to a .txt file present in the same directory the main HTML/CSS/JS file is. Let's call it test.txt.

Now, it changes the <html>'s innerHTML(not the head/body, the html), and the innerHTML is the response from the AJAX request.

test.txt holds HTML code. And within that HTML code there's <script src="another-js-file.js">. This doesn't get executed, this is the problem.

Before you scream at me INNERHTML DOESN'T EXECUTE ANY SCRIPTS I know that. I've seen other answers saying to create a <script> tag within a div that's within yet another div, but it doesn't seem to work with external js files, and the solutions indeed used innerHTML.

Okay, here's a sketch:

 btn.onclick = function(){
   var xhr = new XMLHttpRequest();
   xhr.onreadystatechange = function(){
    if(xhr.readyState = XMLHttpRequest.DONE){
        document.documentElement.innerHTML = this.responseText;
     }
   }
   xhr.open("GET", "./test.txt");
   xhr.send();
   }

Where test.txt holds:

<head>
   <title>Document</title>
   <link rel="stylesheet" href="test.css">
   <script src="another-js-file.js"></script>
</head>

<body>
  <h1>Hello World!</h1>
</body>

No jQuery.

Upvotes: 0

Views: 845

Answers (2)

Kirkify
Kirkify

Reputation: 338

I noticed in your initial XHR request you wrote:

if(xhr.readyState = XMLHttpRequest.DONE)

This should actually be triple === not the single one.

Add this function to your initial JS file where you make the XHR request,

function setInnerHtml(el, html) {
  el.innerHTML = html;
  // Get all the scripts from the new HTML
  const scripts = el.querySelectorAll('script');

  // Loop through all the scripts
  for (let i = 0; i < scripts.length; i++)
  {
    // Create a new script
    const s = document.createElement('script');

    // Go through all the attributes on the script
    for (let j = 0; j < scripts[i].attributes.length; j++) {
      const a = scripts[i].attributes[j];
      // Add each attribute to the new script
      s.setAttribute(a.name, a.value);
    }

    // Incase there is code inside the script tag
    // Example: <script>alert</script>
    s.innerHTML = scripts[i].innerHTML;

    // Append the new script to the head (you could change this to the end of the body as well)
    document.head.appendChild(s);
  }
}

Then when you go to set the innerHTML of the root document object, use the above function instead.

if(xhr.readyState === XMLHttpRequest.DONE){
    setInnerHtml(document.documentElement, this.responseText);
}

Upvotes: 1

MWR
MWR

Reputation: 404

I found that by creating a script element and setting its source to "another-file.js" like this worked:

var script = document.createElement("script");
script.src = "test.js";
document.body.appendChild(script);
THIS GOES AFTER document.documentElement.innerHTML = this.responseText;

Upvotes: 1

Related Questions