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>
tagI am the loaded page
will run but the<script>
wontalert
or any kind of JavaScript
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>
Share Improve this question edited Sep 18, 2019 at 7:48 Ahmad Karimi 1,3733 gold badges15 silver badges27 bronze badges asked Sep 18, 2019 at 7:44 xaander1xaander1 1,1602 gold badges16 silver badges41 bronze badgesThe
<p>
tagI am the loaded page
will run but the<script>
wontalert
or any kind of JavaScript
5 Answers
Reset to default 7Please 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]);
}
});
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);
});
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>
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);
});
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);