We have a Rails application where we are including our application dependencies in the html head within application.js
:
//= require jquery
//= requi
This code will load each script URL put in libs
in the exact order, waiting for one to fully load before adding the next one.
It's not as optimised than letting the browser doing it, but it allow you to monitor the errors and force the order of the loading.
(function(){
var libs = [
"http://example.com/jquery.js",
"http://example.com/tracker.js",
"http://example.com/myscript.js"
];
var handle_error = function() {
console.log("unable to load:", this.src);
};
var load_next_lib = function() {
if(libs.length) {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = libs.shift();
script.addEventListener("load", load_next_lib);
script.addEventListener("error", handle_error);
document.body.appendChild(script);
}
};
load_next_lib();
})();
But I would advise you to check every <script>
tag of your website and see if they have a defer=""
or async=""
attribute.
Most issues come from these because they tell the browser to execute the script later.
They may also just be in the wrong order.
We're still seeing this error a few times a day and haven't reached any conclusive answer to what is causing it. My best guess is that either the earlier scripts time out downloading, or a user is quickly navigating between links, preventing the page from loading completely. They might also just be using the back button causing weird behavior in scripts on the page.
This can happen if you don't wait for the script to load that defines analytics
or if you do not define the order in which the javascript files are loaded. Make sure that the script that defines analytics
is always loaded before you try to call its method track
. Depending on your setup the scripts could load in random order, leading to this unpredictable behavior.
You tried to make sure everything was loaded, but the listener $(document).ready(function () {});
just makes sure that the DOM is ready, not that analytics is available. And here you have the same problem. $
is just jQuery so $ is not defined
means jQuery hasn't been loaded yet. So probably your script came before jQuery was loaded and tried to call what wasn't defined yet.
It has a cool solution. Make js loading correctly for your application. Here, I am loading jQuery first then loading analytics.js . I hope this will solve your problem for html5 supported browsers. Code:
var fileList =[
'your_file_path/jQuery.js',
'your_file_path/analytics.js'
];
fileList.forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
This code snippet will load jQuery first and then load analytics.js file. Hopefully, this will fix your issue.
The basis of your problem probably lies in your assumption:
everything should be loaded synchronously
Everything is most decidedly not loaded synchronously. The HTTP 1.1 protocol supports piplining and due to chunked transfer encoding, your referenced objects may or may not complete loading before your main webpage has finished loading.
All of this happens asynchronously and you can't guarantee the order in which they are loaded. This is why browsers make multiple parallel connections to your web server when loading a single webpage. Because javascript and jQuery are event driven, they are by nature asynchronous which can become confusing if you don't understand that behavior well.
Compounding your problem is the fact that document onload JavaScript event (remember, jQuery just extends JavaScript) "is called when the DOM is ready which can be prior to images and other external content is loaded." And yes, this external content can include your link to the jquery.js script. If that loads after the DOM, then you will see the error "$ is not defined". Because the linked script has not yet loaded, the jquery selector is undefined. Likewise, with your other linked libraries.
Try using $(window).load() instead. This should work when all the referenced objects and the DOM has loaded.
Since scripts tend to load at random orders you may force your analytics script to load after everything is up and ready.
There are various approaches for this task.
HTML5 rocks has given a quite nice snippet for this kind of stuff. Moreover, depending on your needs you may use a module loader like require.js. Using loading promises and Require.js is pretty sweet too. Generally, you use a lot of JavaScript a module loader will help you with this mess.
I kept the ugliest and least efficient, still one solid approach for the end.
Waiting for an external library to load may create huge bottlenecks and should be considered and also handled really carefully. Analytics fetching script is async
and really hard to handle. On these cases I prefer
Loading external deferred scripts is somehow easy:
function loadExtScript(src, test, callback) {
var s = document.createElement('script');
s.src = src;
document.body.appendChild(s);
var callbackTimer = setInterval(function() {
var call = false;
try {
call = test.call();
} catch (e) {}
if (call) {
clearInterval(callbackTimer);
callback.call();
}
}, 100);
}
Take a look at this example where I am loading jQuery dynamically with a promise function when the script is loaded: http://jsfiddle.net/4mtyu/487/
Here is a chaining demo loading Angular and jQuery in order :http://jsfiddle.net/4mtyu/488/
Considering the example above you may load your analytics as:
loadExtScript('https://code.jquery.com/jquery-2.1.4.js', function () {
return (typeof jQuery == 'function');
}, jQueryLoaded);
function jQueryLoaded() {
loadExtScript('https://analytics-lib.com/script.js', function () {
return (typeof jQuery == 'function');
}, analyticsLoadedToo);
}
function analyticsLoadedToo() {
//cool we are up and running
console.log("ready");
}