I have a website where I don't have access to the source but I can manipulate it using Javascript. I have a file called main.js that has been included at the very end of the includes to which I have the access to and I would like to run my custom Javascript code in that file. I have a .JS file with a function called helloWorld() on my server that I would like to load before any $(document).ready() callback fires, because one of the $(document).ready() functions on my website page/pages uses this function.
Custom .JS file:
function helloWorld()
{
alert("Hello World");
}
main.js file on the server (Accessible):
//All the JS code that the website uses
..
// My custom javascript code that includes my custom .JS file
$.getScript("helloWorld.js", function()
{
// Use anything defined in the loaded script...
});
Now I would like the helloWorld() to be loaded whilst the page is loading and before any $(document).ready() functions fired. I understand that loading this .JS file while the page is loading will possibly slow down the page load. Is there a bullet-proof way of making sure that my custom javascript function will be loaded prior to any $(document).ready()'s? If there is any other way I can achieve this, please do let me know. Looking forward to your suggestions.
Looks like I found a solution for your problem. I wouldn't suggest it, but it's the only way you can load an external script from another one before the DOMContentLoaded event fires.
Solution
Since that your main.js script is in the <head> of your document, you can be sure that it will be loaded and executed before any following part of the DOM. Given this, you can use a synchronous XMLHttpRequest to load your script and execute it.
This kind of technique has some pros and cons:
Pros: you can load and execute any script before DOMContentLoaded, and also multiple scripts sequentially.
Cons: your document will be frozen until the requests are completed.
Not that bad, if your script isn't enormous it will not drastically impact the loading time. We can still do it.
Implementation
First of all, make sure that your custom.js script is served over a link which will always be reachable, so that your request will not fail. Also make sure that your main.js script hasn't got async or defer properties, so that it will always be executed in the <head>, before the DOMContentLoaded event.
<!-- NOT GOOD -->
<script src="main.js" defer></script>
<script src="main.js" async></script>
<!-- GOOD :) -->
<script src="main.js"></script>
Now that you're ready, in your main.js script you'll need to:
Create and initialize a synchronous XMLHttpRequest object, and send() a GET request to your content.js script.
Create a <script> element, and put the result of your request (which is stored in the .responseText property of the request object) inside it.
Append the script to the <head> to make it run before the DOM is loaded.
Plus, if you also want your script to be removed right after execution (so it will not be visible to the users), you can:
Remove the <script> from the document after it has ran its code. You'll need to listen for the onload event for this.
Also, if you want to make your code run completely anonymously, you can wrap it inside an anonymous function to prevent the access to it from the global scope.
Here's a quick example of what you'll need to do in your main.js file:
(function() {
// Create the request and the script
var xhr = new XMLHttpRequest(),
s = document.createElement('script');
// Send the request to retrieve custom.js
xhr.open('GET', 'path/to/custom.js', false);
xhr.send();
// Listen for onload, and remove the script after execution
s.addEventListener("load", function(e) {
s.parentElement.removeChild(s);
});
// Load the code inside the script and run it in the head
s.textContent = xhr.responseText;
document.head.appendChild(s);
})();
Now your custom.js script will (anonymously) run before DOMContentLoaded, mission complete!
As far as I can see, there are multiple ways of doing this, but the best way would be to use something like Require.js or CommonJS to resolve your dependencies, concat them, and and publish the resulting concatenated javascript file (or many if you can divide your app into multiple sections).
The not-so-great method would be to use the main script to load other scripts by adding script tags, this way you can ensure its there since its the one loading the other scripts.
Related
There are so many different ways to include JavaScript in a html page. I know about the following options:
inline code or loaded from external URI
included in <head> or <body> tag [1,2]
having none, defer or async attribute (only external scripts)
included in static source or added dynamically by other scripts (at different parse states, with different methods)
Not counting browserscripts from the harddisk, javascript:URIs and onEvent-attributes [3], there are already 16 alternatives to get JS executed and I'm sure I forgot something.
I'm not so concerned with fast (parallel) loading, I'm more curious about the execution order (which may depend on loading order and document order). Is there a good (cross-browser) reference that covers really all cases? E.g. http://www.websiteoptimization.com/speed/tweak/defer/ only deals with 6 of them, and tests mostly old browsers.
As I fear there's not, here is my specific question: I've got some (external) head scripts for initialisation and script loading. Then I've got two static, inline scripts in the end of the body. The first one lets the script loader dynamically append another script element (referencing external js) to the body. The second of the static, inline scripts wants to use js from the added, external script. Can it rely on the other having been executed (and why :-)?
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.
A great summary by #addyosmani
Shamelessly copied from https://addyosmani.com/blog/script-priorities/
The browser will execute the scripts in the order it finds them. If you call an external script, it will block the page until the script has been loaded and executed.
To test this fact:
// file: test.php
sleep(10);
die("alert('Done!');");
// HTML file:
<script type="text/javascript" src="test.php"></script>
Dynamically added scripts are executed as soon as they are appended to the document.
To test this fact:
<!DOCTYPE HTML>
<html>
<head>
<title>Test</title>
</head>
<body>
<script type="text/javascript">
var s = document.createElement('script');
s.type = "text/javascript";
s.src = "link.js"; // file contains alert("hello!");
document.body.appendChild(s);
alert("appended");
</script>
<script type="text/javascript">
alert("final");
</script>
</body>
</html>
Order of alerts is "appended" -> "hello!" -> "final"
If in a script you attempt to access an element that hasn't been reached yet (example: <script>do something with #blah</script><div id="blah"></div>) then you will get an error.
Overall, yes you can include external scripts and then access their functions and variables, but only if you exit the current <script> tag and start a new one.
After testing many options I've found that the following simple solution is loading the dynamically loaded scripts in the order in which they are added in all modern browsers
loadScripts(sources) {
sources.forEach(src => {
var script = document.createElement('script');
script.src = src;
script.async = false; //<-- the important part
document.body.appendChild( script ); //<-- make sure to append to body instead of head
});
}
loadScripts(['/scr/script1.js','src/script2.js'])
I had trouble understanding how to get an embedded module-script to execute before the onload event happens. The answers above helped a lot but let me add a partial answer about what fixed my particular problem of misunderstanding the "Load and execute order of scripts".
I first used ... which caused an odd problem that it worked when loading the page normally, but not when running it in debugger on FireFox. That made debugging very difficult.
Note: Scripts whose type is "module" always have an implicit "deferred" attribute which means they don't stop the parsing of html, which means the onload-event can happen before the script gets executed. I did not want that. But I did want to use type="module" to make my un-exported JavaScript functions and variables invisible to other scripts on the same page.
I tried different options but thanks to the above answers I gained the insight that if you add the async -attribute to a script of type module it means that the script loads asynchronously BUT once it is loaded it executes immediately.
But in my case this was a script embedded in an HTML page. THEREFORE it meant nothing needed to load "asynchronously". It was already loaded with the page, since it was embedded in it. Therefore it with this change did get executed immediately -- which is what I wanted.
So I think it is worthwhile to point out this specific case because it is somewhat counter-intuitive: To get an embedded script executed IMMEDIATELY you must add the ASYNC attribute to its tag.
Ordinarily one might think that "async" means something happens asynchronously, in indeterminate order, not immediately. But the thing to realize is that "async" causes asynchronous LOADING, but immediate EXECUTION after the loading is complete. And when the script is embedded, no loading needs to be done, and therefore you get immediate execution.
Summary: Use
<script type="module" async> ... </script>
to get a module-script embedded to an HTML-page to execute immediately.
Perfect match for your query!
If none of the solutions worked for you then please refer to the below solution of mine which I have developed from my side.
I was also looking for the solution but after searching a lot, I summarized my code as below which is working perfectly for me!!
This is useful when you want functionality such that after previous script has fully loaded then and then only load next script!
just create a file named jsLoadScripts.js and insert it into the head or at the bottom of the body.
//From Shree Aum Software Solutions
//aumsoftwaresolutions#gmail.com
//script incrementor for array
scriptIncrementor = 0;
//define your script urls here
let scripts = [
"https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js",
"jsGlobalFunctions.js",
"jsDateParser.js",
"jsErrorLogger.js",
"jsGlobalVariables.js",
"jsAjaxCalls.js",
"jsFieldsValidator.js",
"jsTableClickEvent.js",
"index.js",
"jsOnDocumentReady.js",
];
//it starts with the first script and then adds event listener to it. which will load another script on load of it. then this chain goes on and on by adding dynamic event listeners to the next scripts!
function initializeScripts() {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = scripts[scriptIncrementor];
document.head.appendChild(script);
script.addEventListener("load", function () {
loadNextScript();
scriptIncrementor++;
});
}
// this function adds event listener to the scripts passed to it and does not allow next script load until previous one has been loaded!
function loadNextScript() {
if (scriptIncrementor != scripts.length - 1) {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = scripts[scriptIncrementor + 1];
document.head.appendChild(script);
script.addEventListener("load", function () {
loadNextScript();
scriptIncrementor++;
});
}
}
// start fetching your scripts
window.onload = function () {
initializeScripts();
};
This may cause you some speed related issues so, you can call function initializeScripts() with your custom needs!
i have no doubts on the parallel scripts loading step, but i would like to know how RequireJS control the execution sequence of the parallel loaded scripts.
example defined as below.
<script data-main="js/main.js" src="js/require.js"></script>
main.js:
define(['a', 'b'], function() {
window.alert("scheduler loaded!");
})
how to ensure that a.js is executed before b.js?
and when the main.js is fetched and parsed for dependencies, how to deal with it later to let it show up in dom as script element without another http fetch. when we add script element to dom, there is always a http fetch(no cache enabled case), isn't it?
Your main.js script actually does get loaded as a <script> tag and run first; it's just that the function inside of it will not run until a and b are loaded. You do not need to add any script tags yourself if you are using requirejs.
To ensure a's content function runs before b's, simply define a as a dependency inside of b
// b.js
define(['a'], function(a) {
// use a for initiation here
}
If you add 'c' to that array, then c will also become an indirect dependency of main.js.
EDIT: In response to comment, this might give a better understanding. Each numbered item is one single HTTP request.
HTML retrieved
Requirejs itself is the first script seen, so that is loaded first.
Requirejs sees that its tag has a data-main attribute, and so retrieves main.js as a <script> tag. Once main.js has loaded, it would simply run the function on the inside of it, BUT, it looks like this file has dependencies (the array) that have not been loaded yet. SO, it creates <script> tags simultaneously for...
a.js
and b.js. Now, let's say this one finishes loading first - maybe a.js is really big. BUT! It looks like b has a dependency - on A. So, it won't run the inside function just yet. Requirejs knows that it's already waiting for a.js's script tag, so it won't send out a separate request for it - it can just wait on it. Once that loads, a's function runs, then b's, then main's.
No HTTP requests are wasted here - Requirejs sends out requests as soon as it's aware of a dependency, and doesn't request individual scripts multiple times, except in rare occasions when it has no way of knowing that something is going to be loaded.
An example of this rare occasion might be if SmallWidget.js is an individual dependency, compiled into a large file called BundleOfWidgets.js. (For a final build, many scripts are often wrapped into one) If one script requests BundleOfWidgets.js but then another script requests SmallWidget.js before that arrives, it won't know it's actually loading SmallWidget.js in a big package, and so it will retrieve that individual file on its own.
Edit: I am looking for a way to async "include" a javascript file inside another.
What if I load a javascript file like this:
<script src="http://my.website.com/file.js" async="" type="text/javascript"></script>
Inside the javascript file I have
$('head').append('<script src="http://other-website.com/other-file.js" type="text/javascript"></script>');
Will this file still be loaded async including the other-file.js?
I am guessing that I need to append it with async like that
$('head').append('<script async="" src="http://other-website.com/other-file.js" type="text/javascript"></script>');
To completely load all javascript async, right?
But inside other-file.js there are functions which I am using inside file.js
Yes you would need to add the async attribute, and if you depend on that other file, you'll need some complex logic to know when it gets loaded.
There are libraries built for this exact purpose, have you looked into them? I could recommend RequireJS, this way you can wrap your code like this:
require(["helper/util"], function(util) {
//This function is called when scripts/helper/util.js is loaded.
//If util.js calls define(), then this function is not fired until
//util's dependencies have loaded, and the util argument will hold
//the module value for "helper/util".
});
It depends on where the JS is running that has the .append() line in it, and whether it has the async attribute.
If it is running in the head of your document, and it is appending to head, then it will not be asynchronous (will block) if the appended <script> tag doesn't have the async attribute.
The reason is that file.js will very likely load and be parsed before the <head> of the document is done being processed. JavaScript will block the browser's HTTP connection until it's done parsing any external JS without async, which means and it will add the new <script> tag below it in the <head> and then proceed to process it and block the HTTP connection, unless the async attribute is present.
I need to load some Javascript dynamically after the page has loaded.
Something like this:
page loads
page adds script element with src = "file1.js"
page adds script element with src = "file2.js"
file2.js has a dependency on file1.js - it adds properties to an object defined in file1.js
The problem is that file2.js is loading first (because it is smaller), and is immediately throwing an error because its dependency doesn't exist.
Is there a way for me to defer evaluation/execution of these new scripts until they have all loaded. (There is actually more than two scripts)
If I were to just embed these scripts in a page normally in authored HTML, then it seems that the browser loads all scripts, then evaluates them. But it is behaving differently because I'm adding script elements on the fly.
Thanks
There's a library called RequireJS that handles exactly this situation, and handles every situation you never realized were problems - http://requirejs.org/docs/start.html
Can't you wrap the contents of the files in functions and call them after everything has loaded?
Two suggestions for you:
Have a look at http://requirejs.org/ It solves this problem, among
others.
Or, roll your own simple js loader function. It would be a function that
uses ajax to load a script and then calls a callback when it's done.
Call this loader function in a nested way so that you load your
scripts in the right order.
I have included 3 external js files at the end of body.
Suppose my document already contains a js named as insertlibs.js and here is the code
var script1 = document.createElement('script');
script1.src='http://code.jquery.com/jquery-latest.js';
script1.type='text/javascript';
document.getElementsByTagName('Body').item(0).appendChild(script1);
// Similar way to include underscore
var script2 = document.createElement('script');
script2.src='hhttp://documentcloud.github.com/backbone/backbone-min.js';
script2.type='text/javascript';
document.getElementsByTagName('Body').item(0).appendChild(script2);
But what is happening sometimes, it is throwing an error that $ is not defined and I tried to debug in Firefox and there is a parallel download occurring for jquery and backbone and sometimes backbone library getting download earlier than jQuery which is causing this error.
As far as i know that if a script tag is included, it will block further request So as soon as I add jquery in dom. I am confused about the workflow here happening.
So i have found the solution, I merged both the js and making a single call which is working perfectly but that does not explain me the flow happening in above case. Please help.
This is because you are attempting to include backbone without ensuring that jquery has been completely loaded. To correct this, you can use the script's onload attribute to attach a callback which will be fired when jquery is loaded.
For ex:
var script1 = document.createElement('script');
script1.src='http://code.jquery.com/jquery-latest.js';
script1.type='text/javascript';
// add an onload handler
script1.onload = function() {
// load the rest of the scripts here
var script2 = document.createElement('script');
script2.src='hhttp://documentcloud.github.com/backbone/backbone-min.js';
script2.type='text/javascript';
document.getElementsByTagName('Body').item(0).appendChild(script2);
}
document.getElementsByTagName('Body').item(0).appendChild(script1);
As far as i know that if a script tag is included, it will block further request
No, the blocking / synchronous download is only when the tags are right in the parsed HTML (or are inserted via document.write during the parse); dynamically DOM-appended scripts load asynchronously and in parallel.
To do that but ensure that scripts are executed when their dependencies are met, you need to use AMD loaders.