document.write fallback causing jQuery to load out of order - javascript

I'm building a new site using HTML5 Boilerplate 4.0, and am running into trouble with its jQuery local fallback code. The code in question is here:
<!-- <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script> -->
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.8.1.min.js"><\/script>')</script>
<script src="js/plugins.js"></script>
<script src="js/main.js"></script>
I'm developing locally, for now, so I've commented out the CDN line. My problem is that jQuery does load, but it loads after plugins.js and main.js, leading to undefined errors.
The closest to maybe an explanation I've found is the #4 point of this previous answer, which suggests this would be expected, but... the above is easily the most used local fallback code for jQuery, and it's H5BP, which is heavily vetted. I must be missing something, yes?

I answered a similar question some time ago.
You can do something like this:
function loadScript(pathToScript, callback) {
if (/jquery/.test(pathToScript) && window.jQuery) {
//jQuery has already been loaded so simply call the callback
callback.apply();
} else {
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.type = "text/javascript";
script.src = pathToScript + "?t=" + new Date().getTime(); //prevent caching
if (callback) {
script.onload = callback;
}
head.appendChild(script);
}
}
var scripts = ['js/vendor/jquery-1.8.1.min.js', 'js/plugins.js', 'js/main.js'];
(function (i) {
if (i < scripts.length) {
var self = arguments.callee;
loadResource(scripts[i], function () {
self(++i);
});
}
})(0);

Neither did I find a precise answer on StackOverflow about this issue, however, it seems that this page covered the subject :
To sum up - creating a truly robust failover solution is not simple. We need to consider browser incompatabilities, document states and events, dependencies, deferred loading and time-outs! Thankfully tools like LABjs exist to help us ease the pain by giving us complete control over the loading of our JavaScript files.
http://happyworm.com/blog/2010/01/28/a-simple-and-robust-cdn-failover-for-jquery-14-in-one-line/
Note : solution relies on the use of LABjs

You may consider using yepnope to load your scripts in parallel with the fallback.
From their website:
yepnope.js has the capability to do resource fallbacks and still
download dependent scripts in parallel with the first.
And the code:
yepnope([{
load: 'http:/­/ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js',
complete: function () {
if (!window.jQuery) {
yepnope('local/jquery.min.js');
}
}
}, {
load: 'jquery.plugin.js',
complete: function () {
jQuery(function () {
jQuery('div').plugin();
});
}
}]);
I hope this help!

Related

"Bundling" solution for HTML/JS only?

I'm sure someone is going to tell me why this is an awful idea, but it may be better than what we have going on already. So all our plugins have been copied/pasted into a Master.js script which is a nightmare to update or sort through. Most of the plugins are minified (which is good) but I'd really like to take the idea of bundling from .net/c#/aspx and implement something of the likes to rid this Master.js file. Copying/pasting new scripts will often freeze the project and nearly everything in it is outdated - so here's my mad science:
In Master.js, create an array of all scripts used:
var scripts = [
'jquery-1.10.2.min.js', // jQuery v1.10.2 | 2005 (https://code.jquery.com/)
'jquery-migrate-1.2.1.min.js', // jQuery Migrate v1.2.1 | 2005 (https://code.jquery.com/)
'jquery-ui.min.js', // jQuery UI v1.10.3 | 2014-09-14 (https://code.jquery.com/)
'bootstrap.min.js', // Bootstrap v3.1.0 | 2014 (http://getbootstrap.com)
];
and create a loop to add scripts to the page:
for (var i = 0; i < scripts.length; i++) {
var link = 'MainJS/' + scripts[i];
LoadJsCssFilePortal(link, 'js');
};
function LoadJsCssFilePortal(filename, filetype, callback) {
// if filename is a external JavaScript file
if (filetype == "js") {
var fileref = document.createElement('script');
fileref.setAttribute("type", "text/javascript");
fileref.setAttribute("src", filename);
};
if (typeof fileref != "undefined") {
// add this script to the document
document.getElementsByTagName("head")[0].appendChild(fileref);
};
};
I also have the function
function LoadAndRunPortalJS(url, callback) {
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState) { //IE
script.onreadystatechange = function () {
if (script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function () {
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
};
which has a build in callback and is called as such
LoadAndRunPortalJS(link, function () {
// when done load next
});
The issue is that the scripts are not finished loading before
the next increment in the loop
the other scripts on the page that are dependent on Master.js
Typically, one could use the jquery deferred object to enforce the order/completion of scripts so this could possibly be used after the first script (jquery) is loaded i > 0 - but this does not solve the issue of the scripts running outside of master.
This is the domain of script loaders/bundlers. You might consider using RequireJS, Webpack for out of the box solutions. If you still want to roll your own, read on.
I'd replace the array of file names you have, with a more resilient structure of the sort
var manifest = [
{key: 'jquery', file: 'jquery-1.10.2.min.js'}
...
}
This should handle any future versioning issues that may popup (it will). The array structure also preserves your loading order (would not recommend such a method).
On page load, only the manifest described above would be loaded. Any on load functions that you would want to call, have to be deferred until scripts are loaded. I would recommend using a method similar to how ad managers (example, google ads) handle it.
var onBootloaderInit = [/* array of functions */];
....
onBootloaderInit.push(
function() {
/* Code inside can now use jQuery or the likes */
}
);
Once loading has finished, you would now simply iterate through the onBootloaderInit array, and execute each function.
To ensure that scripts are loaded in the order you require, and waits for scripts that have been queued, use a script loader (example, LABjs).
$LAB.script('<path to jQuery>').wait(function() {
/* Code that depends on jQuery */
});

Avoiding jQuery conflicts in a dynamically loaded JavaScript application

I am currently trying to develop a JavaScript application that can be embedded in an existing webpage (which I cannot modify). The application needs a specific version of jQuery.
The script used for loading the application is doing the following:
// loading JavaScript needed by my application
(function () {
document.write('<script src="../jquery-1.10.2.min.js"></script>');
document.write(...); // load other scripts (using jQuery 1.10.2)
// storing the application's jQuery in a new namespace
// to avoid conflicts with other jQuery versions
document.write('<script type="text/javascript">' +
'appJQ = jQuery.noConflict(true);</script>'); // error: jQuery is undefined in IE<10
})();
// initializing the application itself
document.onload = function() {
// ...
};
This works fine in every browser I've tested, except IE < 10. In IE 9 and lower I am getting the error that jQuery is undefined.
Moving jQuery to a new namespace in the document.onload function would work for my application but causes conflicts with other scripts on the webpage that includes my application if they need a different version of jQuery.
Do you have any suggestions how to solve this problem?
Thanks for your help!
Instead of using document.write, try creating a <script> element and defining an onload handler for that element:
(function () {
var script = document.createElement('script');
script.src = '//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js';
script.onload = function() {
var appJQ = jQuery.noConflict(true);
// app initialization code
};
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(script);
})();
If you have multiple scripts that depend on one another, you might want to try using a script loader such as HeadJS or LABjs.
If you want even greater flexibility in managing dependencies, you can try using a module loader such as RequireJS, Browserify, or webpack.
Following Hamza's answer, you can use this method of loading in a different way:
var s = document.createElement("script");
s.src = "../jquery.min.js" // insert your own jQuery link
s.onload = function() {
var appJS = jQuery.noConflict(true);
}
Hope this helps.
The document.write calls would also happen after the document is considered loaded, in which case your onload function might fire before the contents of that first function. You want to load the javascript files in a better way, such that their onload initiates the application itself.

Make IE7 load javascript first then call function

IE7 gives the following error: 'myapp' is undefined
//home.html
<script type="application/x-javascript" src="//www.mysite.com/myjs.js"></script>
<script type="text/javascript" charset="utf-8">
new myapp.myfunc();
</script>
javascript file:
//myjs.js
myapp = {
myfunc : function(){
alert('hello world');
}
};
*I understand there are many ways to rewrite the code that is used on home.html, but I want to make this work without changing that. I found a working example with similar structure and no JS errors (IE7, IE6). https://google-developers.appspot.com/custom-search-ads/docs/sample
EDIT:
The < script > code will be given to external clients, so I want to keep it as simple as possible. See example link.
Occam's razor suggests that either IE/MSHTML does not support script elements with type="application/x-javascript". That might have to do with the fact that application/x-javascript is not a registered MIME media type, nor was it ever necessary.
Or it has to do with the fact that //www.mysite.example.com/myjs.js is not a supported URI-reference in that environment. Use fully-qualified URIs like http://www.mysite.example.com/myjs.js instead. (And please use the registered example domains for examples.)
You should also declare identifiers that you intend to use as variables:
var myapp = {
…
};
If you do not do this, problems can occur if there is an element named myapp in the document. In JScript/MSHTML, identifier resolution will find a host object in the scope chain that has myapp as its property. The value of that property will be a reference to the corresponding element object, and attempting to overwrite that property value will cause a runtime error.
Add an event handler to the body's Load event. In that event handler, make your myapp.myfunc() call.
Whenever you're making your code available for consumption, you always want to make sure you're being a good citizen on the page. That means you don't want to create unnecessary global variables, and make sure ones you do create are unique enough. Thats why it's a good idea to wrap your code in an immediately-invoked function expression.
Also, it's generally just easier to do the whole thing with javascript. This is how Google analytics and Facebook plugins load their code.
<script type="text/javascript">
(function() {
var loadScript = function(src, callback) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
// modern browsers
script.onload = callback;
// IE 6 & 7
script.onreadystatechange = function() {
if (this.readyState == 'complete') {
callback();
}
}
head.appendChild(script);
};
loadScript('path/to/myscript.js', function() {
//script loaded
});
})();
</script>

Is script defer reliable?

Currently I serve all javascripts combined in one large file via Amazon Cloudfront. But since jQuery is so large, I'm thinking about using the version provided by Google. Of course I would include both script tags in the bottom of the page and would add the defer attribute, if I had not read this article: http://hacks.mozilla.org/2009/06/defer/
If I understand it right, the defer attribute works only in the Firefox correctly, while every other browser (at that time) would execute the scripts in a random order. Is that true? Of course my scripts depend on jQuery, so it must be executed before my scripts.
In 2017, it seems reliable enough to stop avoiding these 5 letters in favor of 17-liner workarounds. Browser support for defer is quite good these days
I'm not sure I'd bother with defer if you aren't certain of its support. Just do this instead:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script>
<script>window.jQuery || document.write("<script src='localJSFolder/jquery-1.6.4.min.js'></script>")</script>
All modern browsers ought to run the scripts sequentially. This is the easiest way to use Google's CDN with a local fallback.
I would suggest something like this (using window.onload to truly behave like defer):
$(window).load(function () {
var script = document.createElement("script");
script.type = "text/javascript";
if (script.readyState) { // IE
script.onreadystatechange = function () {
if (script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
// do something
}
};
}
else { // Others
script.onload = function() {
// do something (the same thing as above)
};
}
script.src = file;
document.getElementsByTagName("head")[0].appendChild(script);
});
If you need more than one file, put in a loop and set file to something like file[x]

Including a .js file within a .js file [duplicate]

This question already has answers here:
How do I include a JavaScript file in another JavaScript file?
(70 answers)
Closed 9 years ago.
I'd like to know if it is possible to include a .js file within another .js file?
The reason for me wanting to do this is to keep client includes to a minimum. I have several .js files already written with functions that are needed by the client. The client would have an html file which he/she manages with a .js file include (my .js file).
I could re-write a new .js file with all the functions in it or, to avoid doing double work, figure out a way to write a .js file that includes other .js files.
I basically do like this, create new element and attach that to <head>
var x = document.createElement('script');
x.src = 'http://example.com/test.js';
document.getElementsByTagName("head")[0].appendChild(x);
You may also use onload event to each script you attach, but please test it out, I am not so sure it works cross-browser or not.
x.onload=callback_function;
The best solution for your browser load time would be to use a server side script to join them all together into one big .js file. Make sure to gzip/minify the final version. Single request - nice and compact.
Alternatively, you can use DOM to create a <script> tag and set the src property on it then append it to the <head>. If you need to wait for that functionality to load, you can make the rest of your javascript file be called from the load event on that script tag.
This function is based on the functionality of jQuery $.getScript()
function loadScript(src, f) {
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = src;
var done = false;
script.onload = script.onreadystatechange = function() {
// attach to both events for cross browser finish detection:
if ( !done && (!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") ) {
done = true;
if (typeof f == 'function') f();
// cleans up a little memory:
script.onload = script.onreadystatechange = null;
head.removeChild(script);
}
};
head.appendChild(script);
}
// example:
loadScript('/some-other-script.js', function() {
alert('finished loading');
finishSetup();
});
There is no straight forward way of doing this.
What you can do is load the script on demand. (again uses something similar to what Ignacio mentioned,but much cleaner).
Check this link out for multiple ways of doing this:
http://ajaxpatterns.org/On-Demand_Javascript
My favorite is(not applicable always):
<script src="dojo.js" type="text/javascript">
dojo.require("dojo.aDojoPackage");
Google's closure also provides similar functionality.
A popular method to tackle the problem of reducing JavaScript references from HTML files is by using a concatenation tool like Sprockets, which preprocesses and concatenates JavaScript source files together.
Apart from reducing the number of references from the HTML files, this will also reduce the number of hits to the server.
You may then want to run the resulting concatenation through a minification tool like jsmin to have it minified.
I use #gnarf's method, though I fall back on document.writelning a <script> tag for IE<7 as I couldn't get DOM creation to work reliably in IE6 (and TBH didn't care enough to put much effort into it). The core of my code is:
if (horus.script.broken) {
document.writeln('<script type="text/javascript" src="'+script+'"></script>');
horus.script.loaded(script);
} else {
var s=document.createElement('script');
s.type='text/javascript';
s.src=script;
s.async=true;
if (horus.brokenDOM){
s.onreadystatechange=
function () {
if (this.readyState=='loaded' || this.readyState=='complete'){
horus.script.loaded(script);
}
}
}else{
s.onload=function () { horus.script.loaded(script) };
}
document.head.appendChild(s);
}
where horus.script.loaded() notes that the javascript file is loaded, and calls any pending uncalled routines (saved by autoloader code).

Categories

Resources