8

I'm trying to load HTML page with its JavaScript scripts using the fetch API

I could use ajax and JQuery See here to load the page but, is it possible with fetch API?

Here is a code demonstration:

Am assuming your running this on localhost

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>fetch API</title>
</head>
<body>
    <p>index page</p>
    <div id="content">

    </div>

<script>
    fetch('./page.html')
        .then(data => data.text())
        .then(html => document.getElementById('content').innerHTML = html);
</script>
</body>
</html>

the page to be loaded:

<!-- page.html -->
<p> i am the loaded page</p>
<script type="text/javascript">
    alert("Javascript is working");
</script>

The <p> tag I am the loaded page will run but the <script> wont alert or any kind of JavaScript

5 Answers 5

7

Please refer stackoverflow for inserting script into innerhtml .

I have done the changes in index.html and its working for me. Please find the code below

  fetch('./page.html')
.then(function (data) {
  return data.text();
})
.then(function (html) {
  document.getElementById('content').innerHTML = html;
  var scripts = document.getElementById("content").querySelectorAll("script");
  for (var i = 0; i < scripts.length; i++) {
    if (scripts[i].innerText) {
      eval(scripts[i].innerText);
    } else {
      fetch(scripts[i].src).then(function (data) {
        data.text().then(function (r) {
          eval(r);
        })
      });

    }
    // To not repeat the element
    scripts[i].parentNode.removeChild(scripts[i]);
  }
});
Sign up to request clarification or add additional context in comments.

5 Comments

Worked...not to sound whiny but what if i was loading the script in page.html externaly.
@xaander1 Updated the Answer, there may be better approach to achieve the same.
Improved but now it can't load both internal and external JavaScript. If you use JavaScript internally it will ignore the external JavaScript. It would better if it loaded both.
var scripts =document.getElementById("content").querySelectorAll("script"); this should be include though to avoid running scripts in the main... i.e in this case index.html. Just noticed it runs both internal and external JavaScript.
Why scripts[i].parentNode.removeChild(scripts[i]); since no elements are created by the code?
7

I found the other answer using eval() here helpful, but ran into a problem as my dynamically loaded content was importing an external script giving CORS warnings in Firefox, for example:

<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>

I therefore tweaked it a little to inject script tags without using eval() to allow external scripts from other domains as well as embedded scripts. I then modified to load the scripts in the order they are defined, waiting for the load to complete before loading the next - this was due to the fact that an embedded script on my page depended on functionality from the external scripts being loaded.

fetch("/mypage.html").then(response => {
    return response.text();
}).then(data => {

    const app = document.getElementById("app");
    app.innerHTML = data;

    // Load scripts in the order they are defined
    // Note that inserting scripts into an element using innerHTML doesnt work - hence this logic
    var scripts = app.querySelectorAll("script");

    if (scripts !== null && scripts.length > 0) {
        var loadScript = index => {
            if (index < scripts.length) {
                var newScript = document.createElement("script");

                if (scripts[index].innerText) {
                    var inlineScript = document.createTextNode(scripts[index].innerText);
                    newScript.appendChild(inlineScript);
                }
                else {
                    newScript.src = scripts[index].src;
                }
                scripts[index].parentNode.removeChild(scripts[index]);
                newScript.addEventListener("load", event => loadScript(index + 1));
                newScript.addEventListener("error", event => loadScript(index + 1));
                app.appendChild(newScript);
            }
        }

        loadScript(0); // Start loading script 0. Function is recursive to load the next script after the current one has finished downloading.
    }
}).catch(err => {
    console.warn('Something went wrong.', err);
});  

Comments

0

I released this tiny Fetch data loader on Github, which does exactly what you're asking for.

The loadData() function loads the fetched content into a target container then run its scripts. It also accepts callbacks.

A demo is available here: https://www.ajax-fetch-data-loader.miglisoft.com

Here's a sample code:

<script>
document.addEventListener('DOMContentLoaded', function(event) {
    fetch('ajax-content.php')
    .then(function (response) {
        return response.text()
    })
    .then(function (html) {
        console.info('content has been fetched from data.html');
        loadData(html, '#ajax-target').then(function (html) {
            console.info('I\'m a callback');
        })
    }).catch((error) => {
        console.log(error);
    });
});
</script>

Comments

0

based on phil_rawlings's answer I ended up using this piece of code

let scripts = app.querySelectorAll("script");

scripts.forEach((script) => {
    let newScript = document.createElement("script");

    newScript.src = script.src;

    script.parentNode.removeChild(script);

    newScript.addEventListener("load", event => script);
    newScript.addEventListener("error", event => script);

    app.appendChild(newScript);
});

Comments

0

You can use the hmpl-js package to load HTML from the server. It works on fetch, so it can help you avoid writing a bunch of code:

<div id="wrapper"></div>
<script src="https://unpkg.com/hmpl-js"></script>
<script>
const templateFn = hmpl.compile(
    `<div>
       { 
         {
           "src":"http://localhost:8000/api/test" 
         } 
       }
    </div>`
  );

  const wrapper = document.getElementById("wrapper");

  const obj = templateFn();

  wrapper.appendChild(obj.response);
</script>

or

import { compile } from "hmpl-js";

const templateFn = hmpl.compile(
  `<div>
     { 
       {
         "src":"http://localhost:8000/api/test" 
       } 
     }
  </div>`
);

const wrapper = document.getElementById("wrapper");

const obj = templateFn();

wrapper.appendChild(obj.response);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.