I am testing with pure JavaScript if browser seems to support HTML5 and if so, I want to load jQuery and then process the rest of page. If not, some redirection will occur.
<script type="text/javascript">
var canvas = document.createElement('canvas');
if (canvas && canvas.getContext && canvas.getContext('2d')) {
var s = document.getElementsByTagName('script')[0];
var jq = document.createElement('script');
jq.type = 'text/javascript';
jq.src = 'js/jquery.js';
s.parentNode.insertBefore(jq, s);
}
else {
// ... redirection ...
}
</script>
<script type="text/javascript">
$(function () {
//...
}
</script>
But the code above is not working properly, because I got error
Uncaught ReferenceError: $ is not defined
which is clearly saying that jQuery library has not been loaded.
Why? What is wrong with conditional script loading in my code above?
This is a case where it may make sense to use document.write(). You'd need to put this code in the <body> instead of the <head>:
<script type="text/javascript">
var canvas = document.createElement('canvas');
if (canvas && canvas.getContext && canvas.getContext('2d')) {
document.write( '<script src="js/jquery.js"><\/script>' );
}
else {
// ... redirection ...
}
</script>
<script type="text/javascript">
$(function () {
//...
}
</script>
Or, you may be able to use an ordinary <script> tag to load jQuery, but put it after your conditional redirection:
<script>
var canvas = document.createElement('canvas');
if( !( canvas && canvas.getContext && canvas.getContext('2d') ) ) {
// ... redirection ...
}
</script>
<script src="js/jquery.js"></script>
<script>
$(function () {
//...
}
</script>
With either of these approaches, the order of execution is:
The first <script>.
The loading of jquery.js, whether done with document.write() or a simple <script> tag.
The final script.
When you insert a script tag like you are, it will be loaded in the background, not immediately and thus your next script will run before jQuery is loaded. You will need to attach a listener such that you know when jQuery is successfully loaded and you can then run your scripts that use jQuery.
Here's an article that describes how to know when a dynamically loaded script is loaded: http://software.intel.com/en-us/blogs/2010/05/22/dynamically-load-javascript-with-load-completion-notification.
FYI, in your specific case, you also could just have a static script tag that loads jQuery, but place your script that detects whether to redirect or not BEFORE the jQuery script tag. That would be the simplest option.
<script type="text/javascript">
var canvas = document.createElement('canvas');
if (!canvas || !canvas.getContext || !canvas.getContext('2d')) {
// redirect here or whatever
}
</script>
<script type="text/javascript" src="jquery.js">
</script>
<script type="text/javascript">
$(function () {
//...
}
</script>
finally working like a charm, I'm relieved myself !
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
<script type="text/javascript">
window.onload = function(){
var jqu = "$(console.log('worked'));";
var canvas = document.createElement('canvas');
if (canvas && canvas.getContext && canvas.getContext('2d')) {
var s = document.getElementsByTagName('head')[0];
var jq = document.createElement('script');
jq.setAttribute('type','text/javascript');
jq.innerHTML = jqu;
var jqLoad = document.createElement('script');
jqLoad.setAttribute('type','text/javascript');
jqLoad.setAttribute('src','jquery-1.10.0.js');
jqLoad.setAttribute('id','jqloader');
s.appendChild(jqLoad);
document.getElementById('jqloader').onload = function(){
console.log('loaded');
s.appendChild(jq);
}
}
else {
// ... redirection ...
}
console.log(document);
}
</script>
</head>
<body>
</body>
</html>
jsbin Demo
explanation :
1- using dom functions to append or insert elements are always the best (dynamic and safer more than anything else), and document.write is not recommended over that.
2- at parse-time, whatever functions you have in your script will be evaluated thus you will get an error if you have the script and not loaded the library yet.
3- loading the library and executing the relevant script in the same tag is not recommended. better do the script in another tag (after loading is done completely) to ensure it will work.
4- events for document.onload ensures that the document is loaded and the doms exist so you can append children to them. as for the document.getElementById('jqloader').onload it was just to insure that the jquery library is loaded completely and added to the document, and only then the script will be added after and evaluated.
As others have said, the reason you're getting an error is because you've loaded jQuery asynchronously and it hasn't loaded yet.
There are two ways to accomplish what you want.
You can poll for window.jQuery, or you can use an asynchronous loader callback.
Since you only load jQuery only when you detect canvas support, you won't have to worry about supporting old browsers.
var async_script_load = function (s, callback) {
var script;
script = document.createElement("script");
script.async = "async";
if (s.scriptCharset) {
script.charset = s.scriptCharset;
}
script.src = s.url;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function () {
if (!script.readyState || /loaded|complete/.test(script.readyState)) {
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
// Remove the script
if (head && script.parentNode) {
head.removeChild(script);
}
// Dereference the script
script = undefined;
callback(200, "success");
}
};
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
// This arises when a base node is used (#2709 and #4378).
head.insertBefore(script, head.firstChild);
};
async_loader({url:'http://tempuri.org/jquery.min.js'},function() {
//call jquery here.
});
For a polling method, it's as simple as:
var checkJq = function() {
if(window.jQuery) {
//do jQuery
} else {
setTimeout(checkJq,100);
}
}
setTimeout(checkJq,100);
Related
I need to dynamically load a JavaScript file and then access its content.
File test.js
test = function () {
var pub = {}
pub.defult_id = 1;
return pub;
}()
In this case it works:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="/test.js"></script>
</head>
<body>
<script type="text/javascript">
console.log(test.defult_id);
</script>
</body>
</html>
But I need to load it dynamically, and that way it does not work:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script type="text/javascript">
function loadjs(file) {
var script = document.createElement("script");
script.type = "application/javascript";
script.src = file;
document.body.appendChild(script);
}
loadjs('test.js');
console.log(test.defult_id);
</script>
</body>
</html>
Error: Uncaught ReferenceError: test is not defined(…)
You could do it like this:
function loadjs(file) {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = file;
script.onload = function(){
alert("Script is ready!");
console.log(test.defult_id);
};
document.body.appendChild(script);
}
For more information read this article : https://www.nczonline.net/blog/2009/06/23/loading-javascript-without-blocking/
There is a great article which is worth reading for all the guys interesting in js script loading in www.html5rocks.com - Deep dive into the murky waters of script loading .
In that article after considering many possible solutions, the author concluded that adding js scripts to the end of body element is the best possible way to avoid blocking page rendering by js scripts thus speeding page loading time.
But, the author propose another good alternate solution for those people who are desperate to load and execute scripts asynchronously.
Considering you've four scripts named script1.js, script2.js, script3.js, script4.js then you can do it with applying async = false:
[
'script1.js',
'script2.js',
'script3.js',
'script4.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
Now, Spec says: Download together, execute in order as soon as all download.
Firefox < 3.6, Opera says: I have no idea what this “async” thing is, but it just so happens I execute scripts added via JS in the order they’re added.
Safari 5.0 says: I understand “async”, but don’t understand setting it to “false” with JS. I’ll execute your scripts as soon as they land, in whatever order.
IE < 10 says: No idea about “async”, but there is a workaround using “onreadystatechange”.
Everything else says: I’m your friend, we’re going to do this by the book.
Now, the full code with IE < 10 workaround:
var scripts = [
'script1.js',
'script2.js',
'script3.js',
'script4.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];
// Watch scripts load in IE
function stateChange() {
// Execute as many scripts in order as we can
var pendingScript;
while (pendingScripts[0] && pendingScripts[0].readyState == 'loaded') {
pendingScript = pendingScripts.shift();
// avoid future loading events from this script (eg, if src changes)
pendingScript.onreadystatechange = null;
// can't just appendChild, old IE bug if element isn't closed
firstScript.parentNode.insertBefore(pendingScript, firstScript);
}
}
// loop through our script urls
while (src = scripts.shift()) {
if ('async' in firstScript) { // modern browsers
script = document.createElement('script');
script.async = false;
script.src = src;
document.head.appendChild(script);
}
else if (firstScript.readyState) { // IE<10
// create a script and add it to our todo pile
script = document.createElement('script');
pendingScripts.push(script);
// listen for state changes
script.onreadystatechange = stateChange;
// must set src AFTER adding onreadystatechange listener
// else we’ll miss the loaded event for cached scripts
script.src = src;
}
else { // fall back to defer
document.write('<script src="' + src + '" defer></'+'script>');
}
}
A few tricks and minification later, it’s 362 bytes
!function(e,t,r){function n(){for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)}for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s}(document,"script",[
"//other-domain.com/1.js",
"2.js"
])
NOTE: there was one similar solution but it doesn't check if the script is already loaded and loads the script each time. This one checks src property and doesn't add script tag if already loaded.
Loader function:
const loadCDN = src =>
new Promise((resolve, reject) => {
if (document.querySelector(`head > script[src="${src}"]`) !== null) return resolve()
const script = document.createElement("script")
script.src = src
script.async = true
document.head.appendChild(script)
script.onload = resolve
script.onerror = reject
})
Usage (async/await):
await loadCDN("https://.../script.js")
Usage (Promise):
loadCDN("https://.../script.js").then(res => {}).catch(err => {})
Dinamically loading JS files is asynchronous, so to ensure your script is loaded before calling some function inside, use the onload event in script:
function loadjs(file) {
var script = document.createElement("script");
script.type = "application/javascript";
script.onload=function(){
//at this tine the script is loaded
console.log("Script loaded!");
console.log(test);
}
script.src = file;
document.body.appendChild(script);
}
I would like to display a loader icon gif until the script is totally loaded, is it possible?
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<img src="loading.gif">
I think it is necessary to use jquery. but I do not know how to do. could someone help me?
Display image <img src="loading.gif">
On dom-ready remove element.
$(document).ready(function () {
$('img').remove();
})
Can be done even without jQuery:
<img id="loading" src="loading.gif">
<script onload="javascript:document.getElementById('loading').style.display='none'" src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"> </script>
Yes you can. You can use this function which loads a script and fires a callback. In your callback, you hide the gif.
function loadScript( 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 );
}
So, in your HTML place your gif showing up initially. And, add this code at your onload function:
<script>
window.onload = function(){
loadScript('https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js', function(){
$('.gif-selector').hide();
})
}
</script>
Edit: This is a more complex solution than others provided, maybe if your goal is something pretty simple you should use some of the others. Still, this function works great and I have used it in many of my projects. It's best thing is that you can chose to load the script whenever you want to
I'm currently trying to load my javascript file with the following code
window.onload = function()
{
var arr = new Array();
arr[0] = "/js/main.js";
arr[1] = "/js/jquery.expander.min.js";
arr[2] = "/js/custom_expander.js";
for (var i = 0; i < arr.length; i++)
{
var tag = document.createElement("script");
tag.type = "text/javascript";
tag.async = true;
tag.src = arr[i];
document.getElementsByTagName("head")[0].appendChild(tag);
}
};
It's working well but my problem is that my third file (custom_expander.js) depends on the second (jquery.expander.min.js).
That's mostly why custom_expander.js is third in the list by the way.
However, even with this code, I have the following error
TypeError: $(...).expander is not a function
This error is here because my custom_expander.js loaded before jquery.expander.min.js finished loading. When the files are loaded synchronously like below, there's no error.
<script src="/js/main.js" type="text/javascript"></script>
<script src="/js/jquery.expander.min.js" type="text/javascript"></script>
<script src="/js/custom_expander.js" type="text/javascript"></script>
I had the exact same problem when I wanted to load jquery asynchronously and I've decided to keep it in my tag but it's not a solution.
How can I do to load javascript files asynchronously when there's dependencies between files ?
As I posted in above comments, if you are working with jQuery, you can use the $.getScript() method (jQuery Docs)
Here is an example (try it with your own scripts at jsfiddle):
var MyApp = {
Scripts: {
// put them with the correct order
ToLoad: [
'/path/to/script1.js',
'/path/to/script2.js',
'/path/to/script3.js'
],
Loaded: 0
},
DoTheJob: function(){
if( this.Scripts.ToLoad.length == this.Scripts.Loaded ) return;
$.getScript(this.Scripts.ToLoad[this.Scripts.Loaded], function(){
MyApp.Scripts.Loaded++;
MyApp.DoTheJob();
});
}
};
// do it on ready (faster way)
$(function(){
MyApp.DoTheJob();
});
// or do it on load
//window.onload = function(){
// MyApp.DoTheJob();
//});
Remember that you can improve it with a handler when all scripts have been loaded.
I suggest you to use RequireJs.It's cool thing.I think you will like it.it's not difficult
I am trying to include jquery dynamically and i have used the following code-
index.php
<!doctype html>
<html>
<head>
<script type="text/javascript" src="includejquery.js"></script>
</head>
<body>
<div id="testing"></div>
<script type="text/javascript">
$(document).ready(function(){
$('#testing').html('<p>This is a paragraph!</p>');
});
</script>
</body>
</html>
includejquery.js
if(!window.jQuery)
{
var script = document.createElement('script');
script.type = "text/javascript";
script.async = true;
script.src = "http://code.jquery.com/jquery-2.1.1.min.js";
document.getElementsByTagName('head')[0].appendChild(script);
jQuery.noConflict();
}
But jquery functionality is not working it is not printing the paragraph tag :-( Please help me out. Thanks in advance
That's not working because your $(document).ready(... line runs before jQuery loads, and so it fails because either $ is undefined (throwing a ReferenceError) or it refers to something other than jQuery. Also, you're calling jQuery.noConflict() before jQuery is loaded, and if that call did work, it would mean that $ no longer referred to jQuery at all, so $(document).ready(... still wouldn't work.
In any modern browser, you can use the load event on the script element you're adding, which tells you that the script has been loaded. Probably best to pass a callback into a call you make to includejquery.js, like this:
<!doctype html>
<html>
<head>
<script type="text/javascript" src="includejquery.js"></script>
</head>
<body>
<div id="testing"></div>
<script type="text/javascript">
includejQuery(function($){
$('#testing').html('<p>This is a paragraph!</p>');
});
</script>
</body>
</html>
includejquery.js:
function includejQuery(callback) {
if(window.jQuery)
{
// jQuery is already loaded, set up an asynchronous call
// to the callback if any
if (callback)
{
setTimeout(function() {
callback(jQuery);
}, 0);
}
}
else
{
// jQuery not loaded, load it and when it loads call
// noConflict and the callback (if any).
var script = document.createElement('script');
script.onload = function() {
jQuery.noConflict();
if (callback) {
callback(jQuery);
}
};
script.src = "http://code.jquery.com/jquery-2.1.1.min.js";
document.getElementsByTagName('head')[0].appendChild(script);
}
}
Changes there:
In includejquery.js, just define a function and wait to be called.
Have that function accept a callback.
Have it wait for the script to load.
When the script is loaded, call jQuery.noConflict and then, if there's a callback, call it and pass in the jQuery function.
In the HTML, I'm calling the function, and receiving the argument it passes me as $, so within that function only, $ === jQuery even though outside it, it doesn't (because of noConflict).
What's wrong with the implementation from the HTML5-Boilerplate?
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery_2.1.1.min.js"><\/script>')</script>
Alternative solution
(function () {
initScript().then(function (v) {
console.info(v);
var script = document.getElementById("__jquery");
script.onload = function () {
$(document).ready(function () {
// Main logic goes here.
$("body").css("background-color","gray");
});
};
});
function initScript() {
promise = new Promise(function(resolve,reject) {
try {
if(typeof jQuery == 'undefined') {
console.warn("jQuery doesn't exists");
var jQuery_script = document.createElement("script");
jQuery_script.src = "https://code.jquery.com/jquery-2.2.4.min.js";
jQuery_script.type = 'text/javascript';
jQuery_script.id = "__jquery";
document.head.appendChild(jQuery_script);
resolve("jQuery added succesfully.");
}
resolve("jQuery exists.")
} catch (ex) {
reject("Something went wrong on initScript() : ", ex);
}
});
return promise;
}
})();
I used promise because if there is no jQuery in the page we need to wait to load it first.
.ready will not fire since your script loads async.
This should the first thing to run on the page and block all other scripts in order to load the dependencies on time.
Appending to body:
function loadScript() {
var script= document.createElement('script');
script.type= 'text/javascript';
script.src= 'http://www.mydomain/myscript.js';
script.async = true;
document.body.appendChild(script);
}
Appending to head:
function loadScript() {
var head= document.getElementsByTagName('head')[0];
var script= document.createElement('script');
script.type= 'text/javascript';
script.src= 'http://www.mydomain/myscript.js';
script.async = true;
head.appendChild(script);
}
Usually when you include some scripts, browser will load them synchronously, step by step.
But if you set
script.async = true;
script will load asynchronously and other scripts will not waiting for them. To fix this problem you can remove this option.
There is an onload event on the script. Use that.
<!doctype html>
<html>
<head>
<script type="text/javascript">
window.onload = function() {
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "http://code.jquery.com/jquery-2.1.1.min.js";
document.getElementsByTagName('head')[0].appendChild(script);
script.onload = function() {
$('#testing').html('<p>This is a paragraph!</p>');
};
};
</script>
</head>
<body>
<div id="testing"></div>
</body>
</html>
Check your browsers js console. You will probably see something like $ is undefined and not a function. It is because you are running the code in
You can try to wrap the jquery code you want to run in the readyStateChange event of the script tag. Or you can use require.js.
There is a working demo http://jsbin.com/lepapu/2/edit (Click "Run with JS")
<script>
if(!window.jQuery)
{document.write('<script src=http://code.jquery.com/jquery-2.1.1.min.js><\/script>')}
</script>
<script>
$(document).ready(function(){
$('body').html('<p>This is a paragraph!</p>');
});
</script>
The order of scripts matters.
Is there a way (event listener or otherwise) to detect when a particular external javascript is about to load / is loading / has finished loading?
In otherwords, does the browser fire an event when it's about to load, is loading, and/or has finished loading a particular external script?
For my purposes it's not enough to simply check to see if a known object exists or anything like that. Instead, I need something that will detect a JS file is loading/loaded regardless of the contents of the JS file.
The following example works in Chrome. It attaches an handler on the onload event of the head tag and then adds an external javascript file. When the file is loaded, the event is captured and an alert appears.
http://jsfiddle.net/francisfortier/uv9Fh/
window.onload = function() {
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src", "http://code.jquery.com/jquery-1.7.1.min.js");
head.addEventListener("load", function(event) {
if (event.target.nodeName === "SCRIPT")
{
alert("Script loaded: " + event.target.getAttribute("src"));
}
}, true);
head.appendChild(script);
}
Since all browsers blocks the "UI thread" when processing <script> tags, you can rely that pre-existing tags are loaded.
If you are loading a script dynamically, you can listen to the load event of the <script> tag.
function loadScript(src, callback) {
var script = document.createElement("script");
script.setAttribute("src", src);
script.addEventListener("load", callback);
document.getElementsByTagName("script")[0].insertBefore(script);
};
loadScript("http://code.jquery.com/jquery-1.7.1.min.js", function(){
alert("loading is done");
});
<script onload> will fire when a script is finished loading.
You will not be able to do something like:
<script src="/foo.js"></script>
<script src="/bar.js"></script>
<script>
function alertonload(src){
console.log(src+' is loaded');
}
scripts = document.getElementsByTagName('script');
for(var i=0; i<scripts.length; i++){
scripts[i].onload = function(){ alertonload(scripts[i].src); };
}
</script>
This is pure conjecture and speculation; I have not tried it and there's probably better ways to write it, but this will not do what you're looking to do. EDIT: The scripts are loaded as the browser sees them, not after the fact. They will be loaded before this occurs.
I wrote a script for those who wants to call a function after all external files (dynamically added) are loaded. It goes like this:
var file = [];
var loaded = [];
var head = document.getElementsByTagName('head')[0];
var fileOnLoad =
// Pass the arrays to your function
(function(file, loaded){ return function(){
loaded.push(true);
// Print the number of files loaded
document.getElementById("demo").innerHTML +=
"<br>"+loaded.length+" files loaded";
if(file.length == loaded.length){
alert("All files are loaded!");
}
}})(file, loaded);
////////////////////////////////////////////////
//// ////
//// Add the external files dynamically ////
//// ////
////////////////////////////////////////////////
file[0] = document.createElement('script');
file[0].src =
"https://maps.googleapis.com/maps/api/js?v=3.exp";
file[0].onload = fileOnLoad;
head.appendChild(file[0]);
file[1] = document.createElement('script');
file[1].src =
"http://code.jquery.com/jquery-latest.js";
file[1].onload = fileOnLoad;
head.appendChild(file[1]);
file[2] = document.createElement('script');
file[2].src =
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js";
file[2].onload = fileOnLoad;
head.appendChild(file[2]);
file[3] = document.createElement('link');
file[3].rel = "stylesheet";
file[3].href =
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css";
file[3].onload = fileOnLoad;
head.appendChild(file[3]);
file[4] = document.createElement('link');
file[4].rel = "stylesheet";
file[4].href =
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css";
file[4].onload = fileOnLoad;
head.appendChild(file[4]);
<div id="demo">0 file loaded</div>
Hope it helps!
If you can use jQuery, try $.getScript(). Docs here.