Load javascript in consecutive order after browser load event - javascript

What am I trying to do? In an attempt to speed up my website I am loading non-essential javascript after the browser load event. (So the JS files are not render blocking) This is currently functioning correctly.
What is the problem? The problem is sometimes the non-essential javascript depends on other libraries and plus those libraries need to load first.
What have I tried to do to fix the problem? In an attempt to fix the problem I have added a delay event to library dependent javascript. While this works sometimes, the load times of a JS file varies between refreshes and at times can load before the library even with a delay.
QUESTION: Does anyone know of a better way for me the load JS files only after the first JS file has loaded? (See code below)
<script type="text/javascript">
function downloadJSAtOnload() {
var element = document.createElement("script");
var element2 = document.createElement("script");
var delay=40;
element.src = "http://119.9.25.149/sites/all/themes/bootstrap/bootstrap_nova/js/highcharts.js";
element2.src = "http://119.9.25.149/sites/all/themes/bootstrap/bootstrap_nova/js/future-plastic.js";
document.body.appendChild(element);
setTimeout(function(){
document.body.appendChild(element2);
},delay);
}
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
</script>
As you can see from the above, I am trying to load the highcharts js file before I load the future-plastic file.

You're not the first to have this problem, thankfully. There's a lot of difficult solutions around this problem, including using a module loader as suggested in the comment (which I agree is the best long term solution, because they account for more browsers and flexibility, but it's a lot to learn to solve one small problem).
The place to start learning about this problem and the ways to tackle it are all over the web. This is a pretty good resource: http://www.html5rocks.com/en/tutorials/speed/script-loading/
You may want to try defer if you don't have to support Opera Mini or IE9. Or, you can load sync and execute as it loads- their examples is this:
[
'//other-domain.com/1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
The reason why this might work (different browser implement this differently) is because the default is to load dynamically generated script tags are set to async by default, if you set it to false: "they’re executed outside of document parsing, so rendering isn’t blocked while they’re downloaded"

You should use ScriptElement.onload:
var pre = onload;
onload = function(){
if(pre)pre();
var doc = document, bod = doc.body;
function C(t){
return doc.createElement(t);
}
function downloadJSAtOnload(){
var s = C('script'), ns = C('script'), h = doc.getElementsByTagName('head')[0];
var u = 'http://119.9.25.149/sites/all/themes/bootstrap/bootstrap_nova/js/';
s.type = ns.type = 'text/javascript'; s.src = u+'highcharts.js'; h.appendChild(s);
s.onload = function(){
ns.src = u+'future-plastic.js'; h.appendChild(ns);
}
}
downloadJSAtOnload();
}
Note: The first onload is window.onload, since window is implicit.

Related

Cookiebot Cookie Consent Script

I am adding ZohosalesIQ to the CookieBot Prior Consent widget on w WP install.
This script given by zoho is
<script type="text/javascript" data-cookieconsent="statistics">
var $zoho = [];
var $zoho = $zoho || {};
$zoho.salesiq = $zoho.salesiq || {
widgetcode: "1c636a8a8d8e3410b7e579760898b7768f3cb213adb21970788a3891735df801800b6e4a1385c37b0f792b9ee54ce",
values: {},
ready: function() {}
};
var d = document;
s = d.createElement("script");
s.type = "text/javascript";
s.id = "zsiqscript";
s.defer = true;
s.src = "https://salesiq.zoho.eu/widget";
t = d.getElementsByTagName("script")[0];
t.parentNode.insertBefore(s, t);
d.write("<div id='zsiqwidget'></div>");
</script>
I am supposed to be adding <script type="text/plain" data-cookieconsent="statistics">
to the script tag to enable prior consent on cookies created by this script however, when I add this it breaks and fails to load.
Console is empty but the page renders as a white page after pre-load. When I add the code with the jaavscript type tag, it works fine.
I've tried popping itto a call back function but no joy :(
Any pointers would be great.
If the script tag is using the deprecated JavaScript function “document.write” it might cause the problem you have described above because using this function the script is loaded synchronously and it will then clear the window content (generates blank page).
To prevent this issue please make sure that the script is loaded asynchronously since the Cookiebot triggers all scripts asynchronously (i.e. to wait for the visitor’s consent) in order to work properly.
You need rewrite it to use a different approach e.g. “document.write = function(node) {document.body.insertAdjacentHTML(‘beforeend’, node);}”

If I defer JS file loading to improve pagespeed how do i call functions on load?

I'm using this script
<script type="text/javascript">
function downloadJSAtOnload() {
var element = document.createElement("script");
element.src = "defer.js";
document.body.appendChild(element);
}
if (window.addEventListener) {
window.addEventListener("load", downloadJSAtOnload, false);
} else if (window.attachEvent) {
window.attachEvent("onload", downloadJSAtOnload);
} else { window.onload = downloadJSAtOnload; }
</script>
where defer.js is my minified combined file of all my JS functions for the entire site.
this technique is meant to defer the load to prevent the annoying google pagespeed warning:
"Eliminate render-blocking JavaScript and CSS in above-the-fold content"
but now, all my calls for document.ready > function A are now messed up...
is there a fix for it?
Ok,
So I can't say this is best practice but it seems pretty legit:
If you haven't played with Web Workers, I highly suggest at least taking a peek at the API.
<head>
<script
src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous">
</script>
<title>Time for Web Workers!</title>
</head>
<body id="body">
<script type="text/javascript">
var worker = new Worker("js.js");
worker.addEventListener("message",
function (evt) {
var script = document.createElement("script");
script.innerHTML = evt.data;
document.getElementById('body').appendChild(script);
},false);
worker.postMessage(1);
</script>
Web Workers breaks the limit of single threading when using javascript. The big setback with workers are that they cannot access the dom. The most that they can do is compute and pass data via messages.
Ah, but messages!
This is where it gets a bit messy, but stick with me.
var worker = new Worker("js.js");
// any data can be used as arg, but needed to trigger 'messages' listener
worker.postMessage(1);
I first tried to send the postMessage() function to create a new Worker using a local js file. This works, but it causes the jQuery to remain undefined as the script is not availble to the thread. Separate threads, separate resources >_<
To get around this, we need the Worker to send the code back to the main thread to be executed under the dom's jQuery script. We can do this by setting our local js file to listen for the message event and return it's code base, as a string.
This can be done by using a dummy to import your minified local file and parse as a string, or by simply creating a string from your minified script and using it like so:
addEventListener("message",
function (evt) {
var script =
"$(document).ready(
function() {
var thisThing = 'blah';alert(thisThing);
})";
postMessage(script);
}, false);
Back at the main thread, we set an event listener to listen for messages as well. In this function, we need to create a new <script> element and insert the stringified codebase into it before finally appending it to the body.
worker.addEventListener("message",
function (evt) {
var script = document.createElement("script");
script.innerHTML = evt.data;
document.getElementById('body').appendChild(script);
},false);
Note
I have not tested this with a large script
I have only used a local development server to test this
I have not tested any $(document).ready()-heavy code in the local script, so I do not know if this will even fix your issue..
I had fun doing this :P
what about putting your javascript code at the very bottom of the page? that way it will not block any render process.
the code will not be executed because the ready even has been already called, so you should wrap all your code in anonymous functions so it will be executed immediately.
(function(){
//do stuff here
})();

document.attachEvent() not working in IE8

I am aware that in Internet Explorer (Pre-IE9) you cannot use document.addEventListener(), instead you must use document.attachEvent. The problem I'm having is that document.attachEvent('onload', AddExternals); does nothing, at all. In the console, the output should be as follows:
- document.attachEvent
- Adding Externals...
- jQuery loaded!
However in IE8, the console output is:
Is there any obvious reason why this would occur in the below code?
if (document.addEventListener) {
console.log("document.addEventListener")
document.addEventListener("DOMContentLoaded", AddExternals);
} else if (document.attachEvent) {
console.log("document.attachEvent")
document.attachEvent("onload", AddExternals);
}
function AddExternals(){
console.log("Adding Externals...");
var jq = document.createElement("script");
jq.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(jq);
jq.onload = function(){console.log("jQuery loaded!")};
jq.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js";
}
Edit
I have changed document.attachEvent("onload", AddExternals) and now the console is outputting both document.attachEvent and Adding Externals... but the function never completes?
As far as I know, there is no document.onload event. Instead, you would use window.onload as your fallback. You may also have to test the document state to make sure that it is not already loaded (e.g. the events have already fired).
For a completely tested function to know when the document is ready in any level of browser see the code in this prior question/answer: pure JavaScript equivalent to jQuery's $.ready() how to call a function when the page/dom is ready for it
Keep in mind that older versions of IE do not have a .onload for your script tag so you will not necessarily see that console message, but your script should still load. There is a more complicated scheme that will get you notified of when it is loaded for older versions of IE here: javascript notify when script is loaded dynamically in IE
I would suggest you change your script to this:
function AddExternals(){
var doneLoad = false;
function onload() {
if (!doneLoad) {
doneLoad = true;
console.log("jQuery loaded!")
}
}
console.log("Adding Externals...");
var jq = document.createElement("script");
jq.type = "text/javascript";
jq.onload = doneLoad;
jq.onreadystatechange= function () {
if (script.readyState == "loaded" || script.readyState == "complete"){
doneLoad();
}
};
jq.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js";
document.getElementsByTagName("head")[0].appendChild(jq);
}
Relevant changes:
Added support for older method of knowing when the script has loaded.
Made sure there is no duplicate load notification since listening for multiple mechanisms
Set .src before inserting the script tag.

how to know if the javascript, which has been loaded by a javascript, has been loaded [duplicate]

This question already has answers here:
'onload' handler for 'script' tag in internet explorer
(2 answers)
Closed 7 years ago.
I know my subject is quite tricky but i dont know how to much more ellaborate it on the subject alone.
so here how it goes.
i have a button
Load IT!
on the script tag:
function loadTheFile() {
var script = $("<script><\/script>");
script.attr("type", "text/javascript");
script.attr('src','http://www.thisismyexternalloadingjsfile"');
$('body').append(script);
alert("done! the file has been loaded");
}
the script well, when loaded will automatically have a modal box.
but the problem is, my alert seems to fire first than what is one the script
so how will i know if i have finished to load the script?
update for the first attempt to answer:
function loadTheFile() {
var script = $("<script><\/script>");
script.attr("type", "text/javascript");
script.attr('src','http://www.thisismyexternalloadingjsfile"');
$('body').append(script);
$(document).ready(function() {
alert("done! the file has been loaded")};
}
same problem
alert does indeed run before the script has been loaded. All that appending the script tag to the page does is append the script tag to the page. Then the browser has to download the script and, once received, run it. That will be after your loadTheFile function has exited.
So you need to get a callback when the script has actually be loaded and run. This is more standard than it used to be, but still has some cross-browser hassles. Fortunately for you, jQuery's already solved this problem for you (since you're using jQuery already):
function loadTheFile() {
$.getScript('http://www.thisismyexternalloadingjsfile"')
.then(function() {
alert("done! the file has been loaded");
});
}
Re your comment:
but my script file has data-* attributes
Assuming you're talking about data-* attributes on the script tag, then you'll have to do a bit more work, but it's still fairly straightfoward:
function loadTheFile() {
var load = $.Deferred();
var script = document.createElement('script');
script.src = 'http://www.thisismyexternalloadingjsfile"';
// No need for `type`, JavaScript is the default
script.setAttribute("data-foo", "bar");
script.onreadystatechange = function() {
if (script.readyState === "loaded") {
load.resolve();
}
};
script.onload = function() {
load.resolve();
};
load.then(function() {
alert("done! the file has been loaded");
});
document.body.appendChild(script); ;// Or wherever you want to put it
}
The onreadystatechange bit is to handle older versions of IE.
Rather than forge the script with text and jQuery, just use native Javascript:
var s = document.createElement('script');
s.onload = scriptLoaded;
s.src = '/path/to/my.js';
document.body.appendChild(s);
function scriptLoaded() {
console.log('Script is loaded');
}
Try something along these lines:
Your main page:
function whenScriptIsReady(){
alert('This function is called when the other script is loaded in!')
}
function loadTheFile() {
var script = $("<script><\/script>");
script.attr("type", "text/javascript");
script.attr('src','myotherjs.js');
$('body').append(script);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Load IT!
myotherjs.js:
alert('This will automatically run when the JS is loaded in!');
whenScriptIsReady();
JavaScript is executed asynchronously, so you alert will be executed before the browser can load the new script. If you want to execute logic after the script has been loaded, you could add an event listener to your script that will call the function 'loadFunc` once the script load is completed:
var loadFunc = function() {
alert("External Javascript File has been loaded");
};
//Other browsers trigger this one
if (script.addEventListener)
script.addEventListener('load', loadFunc, false);

Loading scripts dynamically

I'm loading a few YUI scripts dynamically in my code in response to an Ajax request. The DOM and the page is fully loaded when the request is made - it's a response for an user event.
I add the <scripts> tag to head as children, but I stumbled in a few problems:
I add two YUI scripts hosted at the Yahoo! CDN and an inlined script of my own responsible for creating object, adding event listeners and rendering the YUI widgets. But I when my script run the YUI scripts are not loaded yet giving me errors and not running as I expect.
There's a way to only run my script (or define a function to be run) when YUI scripts are fully loaded?
Have you tried an onload event?
Edited:(thanks Jamie)
var script = document.createElement("script");
script.type = "text/javascript";
script.src = src;
//IE:
if(window.attachEvent && document.all) {
script.onreadystatechange = function () {
if(this.readyState === "complete") {
callback_function(); //execute
}
};
}
//other browsers:
else {
script.onload = callback_function; //execute
}
document.getElementsByTagName("head")[0].appendChild(script);
If you're using YUI 2.x I highly recommend using the YUI Get utility, as it's designed to handle just this sort of a problem.
If you are loading multiple individual script files from the Yahoo! CDN, you'll need to makes sure both are loaded before executing your dependent code. You can avoid this using the combo handler. See the Configurator to get what the script url should be to load both/all needed YUI files from one url.
http://developer.yahoo.com/yui/articles/hosting/
With that in mind, assuming you must load the YUI files asynchronously, you should use an onload/onreadystatechange handler as noted by digitalFresh.
I would recommend the following pattern, however:
(function (d) {
var s = d.createElement('script'),
onEvent = ('onreadystatechange' in s) ? 'onreadystatechange' : 'onload';
s[onEvent] = function () {
if (("loaded,complete").indexOf(this.readyState || "loaded") > -1) {
s[onEvent] = null;
// Call your code here
YAHOO.util.Dom.get('x').innerHTML = "Loaded";
}
};
// Set the src to the combo script url, e.g.
s.src = "http://yui.yahooapis.com/combo?2.8.1/...";
d.getElementsByTagName('head')[0].appendChild(s);
})(document);
You could use a setTimeout() to run some function that just checks if it's loaded - check something like
if (typeof YUI_NAMESPACED_THING !== "undefined") runCode()
EDIT Thanks, CMS
If I understand this correctly, your ajax response with this:
<script href="yui-combo?1"></script>
<script href="yui-combo?2"></script>
<p>some text here</a>
<script>
// using some of the components included in the previous combos
// YAHOO.whatever here...
</script>
If this is the case, this is a clear case in which you should use dispatcher plugin. Dispatcher will emulate the browser loading process for AJAX responses. Basically it will load and execute every script in the exact order.
Best Regards,
Caridy

Categories

Resources