How can I dynamically add external scripts using jQuery? - javascript

I thought that I could just use jQuery's .append() and add them to the head, but that doesn't seem to be working for my external scripts (Knockout.js).
Here's my code that runs when the page loads. It seems to be working for the stylesheet, but not for the external scripts.
if (window.jQuery === undefined || window.jQuery.fn.jquery !== '1.8.0') {
var script_tag = document.createElement('script');
script_tag.setAttribute("type","text/javascript");
script_tag.setAttribute("src",
"http://code.jquery.com/jquery-1.8.0.min.js");
if (script_tag.readyState) {
script_tag.onreadystatechange = function () { // For old versions of IE
if (this.readyState == 'complete' || this.readyState == 'loaded') {
scriptLoadHandler();
}
};
} else {
script_tag.onload = scriptLoadHandler;
}
// Try to find the head, otherwise default to the documentElement
(document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag);
} else {
// The jQuery version on the window is the one we want to use
jQuery = window.jQuery;
main();
}
function main() {
jQuery(document).ready(function($) {
$("head").append("<script type='text/javascript' src='http://knockoutjs.com/js/jquery.tmpl.js'></script>");
$("head").append("<script type='text/javascript' src='http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-1.2.1.js'></script>");
$("head").append("<link href='style.css' rel='stylesheet' type='text/css' />");
// Then it appends the necessary HTML code [...]
});
}
Here's my test environment where you can see my current code in action with Firebug.
Here's what I'm seeing in Firebug after the page loads:
EDIT: It looks like it's having issues with the Knockout.js scripts in my code, so I'll look into those. Thank you for the comments and answer regarding dynamic scripts. I learned something :)

Have you tried jQuery.getScript() ? It basically loads a script from the server and then executes it.
$.getScript("yourScript.js", function(){});

Try to add scripts this way, I have seen that issue before in some browsers.
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = script_url;
$("head").append( script );

According to this jQuery API pages comment here, this behavior is perfectly normal, as jQuery cleans up the DOM after you
Your code is executed (if URL is correct, and XSS is not blocked) whether you fetch it by $.append()ing it or use $.getScript().
However, loading your site gives me at least three two solid errors. You might want to work on those.
The errors:
ReferenceError: $ is not defined
search.js
Line 54
and
TypeError: jQuery is undefined
http://knockoutjs.com/js/jquery.tmpl.js?_=1353351345859
Line 7

You should use something like AngularJS if your application is this complex. Otherwise you are reinventing the wheel.

Related

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.

How to include jQuery in remote Javascript file only if site doesn't have jQuery?

I have a script (embed.js) that is being included from other websites/domains. Ex:
<script type="text/javascript" src="//mydomain.com/embed.js"></script>
The code in this script relies on jQuery, but obviously not every website uses jQuery. So, I put this code in the embed.js script at the top:
if (typeof jQuery == 'undefined') {
var jq = document.createElement('script');
jq.type = 'text/javascript';
jq.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js';
document.getElementsByTagName('head')[0].appendChild(jq);
jQuery.noConflict();
}
But, I get an error about "jQuery" not being defined for the noConflict() line. It is indeed adding the jQuery tag/code to the page, but for some reason after that appendChild() line, the following noConflict() line doesn't recognize jQuery.
How do I only include jQuery on a page if it's not already on it, through a remote Javascript file, and then in that same Javascript file use that jQuery I just included? (This all needs to be through one file.)
You are getting the error because the method you're using for loading jQuery loads it asynchronously, thus jQuery is not yet loaded when you try to execute the .noConflict() line.
You would have similar problems if your code is also trying to use jQuery as it initializes (if your code isn't also loading asynchronously).
If you need to load jQuery synchronously (simpler solution, but probably not preferred), then you can use document.write() to actually write the script tag that would load it and the browser will process that synchronously.
You can also load jQuery in a way that will notify you when it is actually loaded at which time you can run the .noConflict() line and then call your own initialization code that uses jQuery. This is probably the most self-contained mechanism and avoids document.write() which can slow down the loading process some in modern browsers (use of document.write() in some circumstances prevents some loading optimizations).
For example, you could load jQuery with this function and then it would call your callback when it was loaded successfully:
function loadScript(sScriptSrc, oCallback) {
var oHead = document.getElementsByTagName('head')[0];
var oScript = document.createElement('script');
oScript.type = 'text/javascript';
oScript.src = sScriptSrc;
// most browsers
oScript.onload = oCallback;
// IE 6 & 7
oScript.onreadystatechange = function() {
if (this.readyState == 'complete') {
oCallback();
}
}
oHead.appendChild(oScript);
}
loadScript("jquery.js", function() {
jQuery.noConflict();
// call your own initialization function that uses jQuery here
});

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>

document.write fallback causing jQuery to load out of order

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!

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]

Categories

Resources