Inject a Script Tag with Remote Src and Wait for It to Execute

Inject a script tag with remote src and wait for it to execute

You could use Google Analytics or Facebook's method:

(function(d, script) {
script = d.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.onload = function(){
// remote script has loaded
};
script.src = 'http://www.google-analytics.com/ga.js';
d.getElementsByTagName('head')[0].appendChild(script);
}(document));

UPDATE:

Below is the new Facebook method; it relies on an existing script tag instead of <head>:

(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)){ return; }
js = d.createElement(s); js.id = id;
js.onload = function(){
// remote script has loaded
};
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
  • Replace facebook-jssdk with your unique script identifier to avoid it being appended more than once.
  • Replace the script's url with your own.

Executing a script tag on append after the DOM has loaded

On a whim I made this change:

// document.getElementById('scriptMe').appendChild(s);
document.body.appendChild(s);

and boom, script runs and video loads.

Which is super interesting, because "why", right?

Edit:
In addition, trying other script injection methods discussed here.

document.write method

document.write(s.outerHTML) // s is a script node

also works. In fact, you can embed that script node in a div and it works as well.

createContextualFragment method

// var $container = document.getElementById('scriptMe'); // does not work
var $container = document.body
var range = document.createRange()
$container.appendChild(range.createContextualFragment(script_str))

works, where script_str is an html string literal. This will work both as "<script>....</script>" or "<div id="myDiv"><script>...</script></div>"

but all the methods I tested ultimately needed injection to be done in body.

codepen

Loading javascript dynamically - wait for execution?

JavaScript is neither threaded nor event-interrupted. The hole script is executed before anything else can happen. Events are captured until the browser gets back the control. So onload or any other event can only be fired before or after the script execution is done. Actually onload is fired after the execution.

There is an event beforescriptexecute still supported by firefox, however, it has been remove from HTML5.1 specs.

You can try it out yourself:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>

console.log('base script start');

var script=document.createElement('script');
script.onload = function() { console.log('onload fired'); }

// MDN says:
// This event was a proposal in an early version of the specification. Do not rely on it.
//
script.addEventListener('beforescriptexecute', function () { console.log('beforescriptexecute fired'); });

script.src = 'external.js';
document.head.appendChild(script);

console.log('waiting 3 seconds');
timebase = Date.now();
while((Date.now() - timebase) < 3000)
;
console.log("base script end");
</script>

</body>
</html>

external.js:

console.log('external start... waiting 3 seconds');
timeext = Date.now();
while((Date.now() - timeext) < 3000)
;

console.log('external end');

base script start

waiting 3 seconds

base script end

beforescriptexecute fired

external start... waiting 3 seconds

external end

onload fired

MDN - beforescriptexecute

Waiting for dynamically loaded script

It is pretty safe. Historically, <script> tags are full blocking, hence the second <script> tag can't get encountered befored the former has finished parsing/excuting. Only problem might be that "modern" browsers tend to load scripts asynchronously and deferred. So to make sure order is correct, use it like this:

<p>Loading jQuery</p>
<script type='text/javascript' async=false defer=false src='scripts/jquery/core/jquery-1.4.4.js'></script>
<p>Using jQuery</p>
<script type='text/javascript'>
$.ajax({
...
});
</script>

However, it's probably a better idea it use dynamic script tag insertion instead of pushing this as HTML string into the DOM. Would be the same story

var scr  = document.createElement('script'),
head = document.head || document.getElementsByTagName('head')[0];
scr.src = 'scripts/jquery/core/jquery-1.4.4.js';
scr.async = false; // optionally

head.insertBefore(scr, head.firstChild);

load and execute order of scripts

If you aren't dynamically loading scripts or marking them as defer or async, then scripts are loaded in the order encountered in the page. It doesn't matter whether it's an external script or an inline script - they are executed in the order they are encountered in the page. Inline scripts that come after external scripts are held until all external scripts that came before them have loaded and run.

Async scripts (regardless of how they are specified as async) load and run in an unpredictable order. The browser loads them in parallel and it is free to run them in whatever order it wants.

There is no predictable order among multiple async things. If one needed a predictable order, then it would have to be coded in by registering for load notifications from the async scripts and manually sequencing javascript calls when the appropriate things are loaded.

When a script tag is inserted dynamically, how the execution order behaves will depend upon the browser. You can see how Firefox behaves in this reference article. In a nutshell, the newer versions of Firefox default a dynamically added script tag to async unless the script tag has been set otherwise.

A script tag with async may be run as soon as it is loaded. In fact, the browser may pause the parser from whatever else it was doing and run that script. So, it really can run at almost any time. If the script was cached, it might run almost immediately. If the script takes awhile to load, it might run after the parser is done. The one thing to remember with async is that it can run anytime and that time is not predictable.

A script tag with defer waits until the entire parser is done and then runs all scripts marked with defer in the order they were encountered. This allows you to mark several scripts that depend upon one another as defer. They will all get postponed until after the document parser is done, but they will execute in the order they were encountered preserving their dependencies. I think of defer like the scripts are dropped into a queue that will be processed after the parser is done. Technically, the browser may be downloading the scripts in the background at any time, but they won't execute or block the parser until after the parser is done parsing the page and parsing and running any inline scripts that are not marked defer or async.

Here's a quote from that article:

script-inserted scripts execute asynchronously in IE and WebKit, but
synchronously in Opera and pre-4.0 Firefox.

The relevant part of the HTML5 spec (for newer compliant browsers) is here. There is a lot written in there about async behavior. Obviously, this spec doesn't apply to older browsers (or mal-conforming browsers) whose behavior you would probably have to test to determine.

A quote from the HTML5 spec:

Then, the first of the following options that describes the situation
must be followed:

If the element has a src attribute, and the element has a defer
attribute, and the element has been flagged as "parser-inserted", and
the element does not have an async attribute

The element must be added
to the end of the list of scripts that will execute when the document
has finished parsing associated with the Document of the parser that
created the element.

The task that the networking task source places on the task queue once
the fetching algorithm has completed must set the element's "ready to
be parser-executed" flag. The parser will handle executing the script.

If the element has a src attribute, and the element has been flagged
as "parser-inserted", and the element does not have an async attribute

The element is the pending parsing-blocking script of the Document of
the parser that created the element. (There can only be one such
script per Document at a time.)

The task that the networking task source places on the task queue once
the fetching algorithm has completed must set the element's "ready to
be parser-executed" flag. The parser will handle executing the script.

If the element does not have a src attribute, and the element has been
flagged as "parser-inserted", and the Document of the HTML parser or
XML parser that created the script element has a style sheet that is
blocking scripts
The element is the pending parsing-blocking script of
the Document of the parser that created the element. (There can only
be one such script per Document at a time.)

Set the element's "ready to be parser-executed" flag. The parser will
handle executing the script.

If the element has a src attribute, does not have an async attribute,
and does not have the "force-async" flag set
The element must be added
to the end of the list of scripts that will execute in order as soon
as possible associated with the Document of the script element at the
time the prepare a script algorithm started.

The task that the networking task source places on the task queue once
the fetching algorithm has completed must run the following steps:

If the element is not now the first element in the list of scripts
that will execute in order as soon as possible to which it was added
above,
then mark the element as ready but abort these steps without
executing the script yet.

Execution: Execute the script block corresponding to the first script
element in this list of scripts that will execute in order as soon as
possible.

Remove the first element from this list of scripts that will execute
in order as soon as possible.

If this list of scripts that will execute in order as soon as possible
is still not empty and the first entry has already been marked as
ready, then jump back to the step labeled execution.

If the element has a src attribute The element must be added to the
set of scripts that will execute as soon as possible of the Document
of the script element at the time the prepare a script algorithm
started.

The task that the networking task source places on the task queue once
the fetching algorithm has completed must execute the script block and
then remove the element from the set of scripts that will execute as
soon as possible.

Otherwise The user agent must immediately execute the script block,
even if other scripts are already executing.


What about Javascript module scripts, type="module"?

Javascript now has support for module loading with syntax like this:

<script type="module">
import {addTextToBody} from './utils.mjs';

addTextToBody('Modules are pretty cool.');
</script>

Or, with src attribute:

<script type="module" src="http://somedomain.com/somescript.mjs">
</script>

All scripts with type="module" are automatically given the defer attribute. This downloads them in parallel (if not inline) with other loading of the page and then runs them in order, but after the parser is done.

Module scripts can also be given the async attribute which will run inline module scripts as soon as possible, not waiting until the parser is done and not waiting to run the async script in any particular order relative to other scripts.

There's a pretty useful timeline chart that shows fetch and execution of different combinations of scripts, including module scripts here in this article: Javascript Module Loading.

Insert script to a div via innerHTML working but not loading

There is no innerText in an external script. Also innerHTML does not render scripts

You likely want to do this:

var myDiv = document.getElementById("mydiv");var oldWrite = document.write, html=[];document.write = function(str) {  html.push(str);}var script = document.createElement("script");script.src="https://gist.github.com/raselahmed7/e233c308d5bf354c9d174f80a30c6b6a.js"document.getElementsByTagName("head")[0].appendChild(script)setTimeout(function() { myDiv.innerHTML=html.join("\n") },1000)
<div id="mydiv"></div>


Related Topics



Leave a reply



Submit