How to dynamically add script which will be executed right now? - javascript

There is the simple page:
<html>
<head>
<script type="text/javascript" src="scripts/x.js"></script>
</head>
<body>
<script type="text/javascript">
console.log($);
</script>
</body>
</html>
'scripts.x.js':
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://code.jquery.com/jquery-1.12.3.min.js';
script.async = false;
script.defer = false;
document.currentScript.parentNode.insertBefore(script, document.currentScript.nextSibling);
This script just adds a new external script tag right after current script, but it doesn't work, because 'console.log' writes error 'index.html:8 Uncaught ReferenceError: $ is not defined'. What am I doing wrong? Thanks in advance!

The problem is that because of the asynchronous I/O model of JavaScript the script doesn't do what you expect. You probably think the script will stop executing while jQuery is loading, then continue once the external script (https://code.jquery.com/jquery-1.12.3.min.js in your case) is loaded.
What actually happens is that the I/O operation is done by another thread and by the time the JS interpreter reaches the console.log call, the I/O hasn't finished yet and therefore $ hasn't been initialized yet.
I suggest that you try requirejs - it handles loading of external scripts asynchronously.

Related

Loading external script after a page has loaded

If I include this script tag in the header or the body of an HTML document, then the external script it points to will be executed:
<script type="text/javascript" src="http://www.example.com/script.js"></script>
If I add that same script tag to the page AFTER it has finished loading, either by using another script in the page or by running a javascript: URI, the external script will not load.
This is an HTML document that tries to do what I'm talking about:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script type="text/javascript">
function f () {
var s;
s = document.createElement("script");
s.setAttribute("type", "text/javascript");
s.setAttribute("src", "https://code.jquery.com/jquery-3.4.1.min.js");
document.body.appendChild(s);
alert(typeof $);
}
</script>
</head>
<body onload="f();">
</body>
</html>
If you open this document in a web browser, the JavaScript pop-up dialogue will say "undefined" instead of "object". If it said "object", then that would mean that the jQuery code had been loaded.
Another case would be a bookmarklet that requires JavaScript code that is not used by the page it is run on. For example, if a bookmarklet needs jQuery and the page that it is run on does not use jQuery, it might do this:
s = document.createElement("script");
s.setAttribute("type", "text/javascript");
s.setAttribute("src", "https://code.jquery.com/jquery-3.4.1.min.js");
document.body.appendChild(s);
The above code does not result in jQuery being loaded.
What can I do to load a script after an HTML page has loaded? I do not want to use any JavaScript libraries because that would require the library code to have already been loaded by the page.
Appending scripts with javascript loads them asynchronously. Add an onload handler to execute what code you want
<script type="text/javascript">
function f () {
var s;
s = document.createElement("script");
s.setAttribute("type", "text/javascript");
s.setAttribute("src", "https://code.jquery.com/jquery-3.4.1.min.js");
document.body.appendChild(s);
s.onload=function(){alert(typeof $)};
}
</script>
What can I do to load a script after an HTML page has loaded?
Put
<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js">
after the closing of the body. An html page is rendered from top to bottom so only when the body has been loaded the browser will download the script (if not already in cache). In this way you can put another script after this to console log typedef if your really need it

Registering an async javascript, declarative (static) vs dynamic

Is there any difference in declaring my async javascript statically vs dynamically?
static
<html>
<head>
...
</head>
<body>
...
<div id='my-script-needs-me'></div>
<script type="text/javascript" src="https://foo.bar/myscript.js" async>
</script>
...
</body>
</html>
dynamic
<html>
<head>
...
</head>
<body>
...
<div id='my-script-needs-me'></div>
<script type="text/javascript">
var myScript = document.createElement("script");
myScript.src = 'https://foo.bar/myscript.js';
myScript.async = !0;
myScript.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(myScript);
</script>
...
</body>
</html>
I noticed that declaring a script statically let a browser detect it earlier and preload (chrome + firefox).
My goal is to load a javascript in async way in order not to block HTML rendering and other scripts execution. Sametime, I want it to be executed as soon as it's downloaded, having in mind that it requires one element to be in the DOM already. Once downloaded the script is executed and it accesses the my-script-needs-me div. One limitation, I cannot change the script itself.
supports async parameters allowing to make this call asynchronous.
The second way you described allows you to have the url as a parameter and bind it.
It allows too the use of a callback to do some stuff when your script is loaded.
let scriptElement = document.createElement('script');
let url = `https://maps.googleapis.com/maps/api/js?key=${apiKey}`;//&libraries=geometry
scriptElement.src = url;
//Chargement de l'API Google
scriptElement.onload = () => {
//API chargée, on peut lancer l'initialisation du composant
this._initializeMap();
};
I used this to load Google Maps API, it's not directly in the HTML, so i can modify the URL when my page loads. And when the API is loaded, I an launch treatments that need this API.
you can use defer for that instead of async.
your script will execute right after html be parsed.
Static
<html>
<head>
...
</head>
<body>
...
<div id='my-script-needs-me'></div>
<script type="text/javascript" src="https://foo.bar/myscript.js" async>
</script>
...
</body>
</html>
As you know, HTML is parsed top-bottom. So, if it placed within body tag, then as soon as parsed, if it is an IIFE or the file myscript.js contains a function call, it will execute immediately.
So, inside, body, put it the script at the bottom will help you to execute it after the div has loaded.
But we can't ensure because of caching.
If the browser cache the script and if it is an IIFE or contains a function call, we can't predict the behaviour.
Dynamic
In dynamic also, it depends on the order.
<html>
<head>
...
</head>
<body>
...
<div id='my-script-needs-me'></div>
<script type="text/javascript">
var myScript = document.createElement("script");
myScript.src = 'https://foo.bar/myscript.js';
myScript.async = !0;
myScript.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(myScript);
</script>
...
</body>
</html>
In both cases, it will render after HTML contents.
The best way to ensure it loads only after all contents are loaded is
Giving an eventListener on Window.
Check the code below
<html>
<head>
...
</head>
<body>
...
<div id='my-script-needs-me'></div>
<script type="text/javascript">
function load(){
var myScript = document.createElement("script");
myScript.src = 'https://foo.bar/myscript.js';
myScript.async = !0;
myScript.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(myScript);
}
window.addEventListener("DOMContentLoaded",load);
</script>
...
</body>
</html>
Check this line window.addEventListener("DOMContentLoaded",load);.
The DOMContentLoaded is similar to jQuery's $(document).ready(). It will trigger the callback function when the HTML is properly loaded. So, you don't have to check for the existence of the HTML Element.
From what I've learned it's better to go with static way to declare an async script (in my particular scenario) than dynamic. Here some of why(s):
static async script declaration is detected by a browser and kicked off right away (at the very top of the page processing);
(deferred from #1) a browser puts the script request earlier in requests queue and if you have enough (30-40 requests per page load) it could be crucial to be in first 10 requests, not at the position 30-40;
adding a script dynamically to the head from the body doesn't introduce any performance advantage against the static declaration as long as whole head is already processed and it won't delay execution of the statically declared script;
at the moment when we reach the script declaration, static will work instantly because it's already pre-loaded and ready to be executed (in most cases, async is crucial here) while the dynamic script declaration will just kick off the request to download the script and only after then execute it;
I hope my thoughts will help someone as well.

Loading another script from a Javascript file but I can't access functions in the loaded script

I've been trying to load a file using the following code, in a file I've called InterpolatorTest.js:
include("scripts/ProceduralContentGeneration/NoiseGeneration/Interpolation/Interpolator.js");
var interpolator = new OneDimensionalInterpolatorTemplate();
This is the include code, which is loaded in the header of every page:
function include(scriptName) {
var script = document.createElement('script');
script.type = "text/javascript";
script.src = scriptName;
document.getElementsByTagName("head")[0].appendChild(script);
}
And this is the script I'm trying to include, Interpolator.js:
OneDimensionalInterpolatorTemplate = function() {
}
However, when I try to load the script, I get the following error:
Uncaught ReferenceError: OneDimensionalInterpolatorTemplate is not defined --- InterpolatorTest.js:3
It doesn't seem to be able to access the methods in the loaded script, but it doesn't give me a 404 error so I assume it's loading the script successfully. I've used this include script before with success.
This is the actual code for the page:
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Page Title</title>
<script type="text/javascript" src="scripts/EssentialUtilities.js"></script>
</head>
<body>
<script type="text/javascript" src="scripts/ProceduralContentGeneration/NoiseGeneration/Interpolation/InterpolatorTest.js"></script>
</body>
</html>
I've been working on this for hours and I'm totally stumped. Anyone know what's wrong?
Edit: Thanks to the answer from Alberto Zaccagni, I got it working by installing jQuery and using this code snippet. The issue seems to be that Javascript loads files asynchronously, so I was attempting to call the method before it was loaded. AJAX has a synchronous loading method, so that eliminated the problem. :)
I think that what you're missing is the onload bit, have a look at this question: How do I include a JavaScript file in another JavaScript file?
I include the relevant snippet as reference:
function loadScript(url, callback)
{
// Adding the script tag to the head as suggested before
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
// Then bind the event to the callback function.
// There are several events for cross browser compatibility.
script.onreadystatechange = callback;
script.onload = callback;
// Fire the loading
head.appendChild(script);
}

Page loading blocks by remote javascript

I am loading external javascript on my site through:
<script language="javascript" type="text/javascript">
document.write("<script src='http://remoteserver/banner.php?id=10&type=image&num=3&width=200'><\/script>");
</script>
Everything was fine, but today was a big timeout from the remote server and page loading on site was blocking.
Is there possibility to load javascript asynchronous or to load it after page loads?
I don't want page load interruption while remote server not working.
I know there are tag async in HTML5, but it's not working, i think because this script is more complex than javascript (its ending with ".php").
Also i know it's better to put javascript files before /body tag to prevent this problem, but it's not the best solution.
Do you know other solutions?
Sync script blocks the parser...
Incorrect Method:
<script type="text/javascript" src="https://apis.google.com/js/plusone.js">
</script>
Async script will not block the rendering of your page:
Correct method:
<script type="text/javascript">
(function(){
var po = document.createElement('script');
po.type = 'text/javascript';
po.async = true;
po.src = "https://apis.google.com/js/plusone.js";
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(po, s);
})();
</script>
Source: http://www.youtube.com/watch?feature=player_detailpage&v=Il4swGfTOSM&t=1607
You can easily do it by creating DOM nodes.
<script>
var scriptEl = document.createElement("script"); //Create a new "script" element.
scriptEl.src = "http://remoteserver/banner.php?id=10&type=image&num=3&width=200";
// ^ Set the source to whatever is needed.
document.body.appendChild(scriptEl);
// Append the script element at the bottom of the body.
</script>

Browser event when all JS files loaded

My AJAX app is basically one index.html plus a bunch of .js modules. My setup function hooks up the js handler code to the appropriate DOM element. I suspect I need to use window.onload() rather than jquery's $(document).ready() since all the .js files need to be available (i.e. downloaded) at hookup time.
My understanding is that only the DOM tree is ready at $(document).ready(), but there's no guarantee that the .js files have been loaded. Is that correct?
PS. I don't need multiple onload handlers; a single window.onload() is fine for me.
You definitely have a misunderstanding in this case. The whole reason why it is considered best practice to include your script tags just before the close of the body tag is because script loads are blocking loads. Unless specifically coded otherwise (i.e.; google analytics), JavaScript files are loaded synchronously.
That said, if there are dependencies between script files, the order in which the files are loaded can be important.
No, you can safely use $(document).ready() as long as your script tags are loaded synchronously (in most cases, this means "normally"). The browser waits for a <script> to be loaded before continuing. Therefore, $(document).ready() includes all <script> tags in the source.
There are two exceptions to this, if the script tags contains an async or defer attribute. The prior meaning the compatible browsers can continue rendering the page, and the latter signifying that the script is executed when the page has finished.
I setup two files as a test:
syncscript.html
<html>
<head>
<title></title>
<style type="text/css">
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript">
$(window).load(function(){
$(document.body).append('<p>window.load has run.');
});
$(document).ready(function(){
$(document.body).append('<p>document.ready has run.');
});
</script>
</head>
<body>
<p>Page has loaded. Now continuing page load and attempting to load additional script file (after 10 second pause).</p>
<script type="text/javascript">
var p = document.createElement('p');
p.innerHTML = '<p>Inline script preceding jssleep.php file has run.</p>';
document.body.appendChild(p);
</script>
<script type="text/javascript" src="http://jfcoder.com/test/jssleep.php"></script>
<script type="text/javascript">
var p = document.createElement('p');
p.innerHTML = '<p>This is an inline script that runs after the jssleep.php script file has loaded/run.</p>';
document.body.appendChild(p);
</script>
</body>
</html>
jssleep.php
<?php
header("content-type: text/javascript");
sleep(10);
?>
var p = document.createElement('p');
p.innerHTML = '<p>jssleep.php script file has loaded and run following <?php sleep(10); ?>.</p>';
document.body.appendChild(p);
This outputs (in Firefox):
Page has begun loading. Now continuing page load and attempting to
load additional script file (after 10 second pause).
Inline script preceding jssleep.php file has run.
jssleep.php script file has loaded and run following <?php sleep(10);
?>.
This is an inline script that runs after the jssleep.php script file
has loaded/run.
$(document).ready() has run.
$(window).load() has run.
That is correct. However window.onload also requires CSS and images to be downloaded, so may be a bit overkill.
What you can do is this:
var scriptsToLoad = 0;
// for each script:
s = document.createElement('script');
s.type = "text/javascript";
s.src = "path/to/file.js";
scriptsToLoad += 1;
s.onload = function() {scriptsToLoad -= 1;};
// after all scripts are added in the code:
var timer = setInterval(function() {
if( scriptsToLoad == 0) {
clearInterval(timer);
// do stuff here
}
},25);

Categories

Resources