I have an external js file being loaded (which I cannot modify) with document.createElement() and I need to access a variable from it. The problem is, I don't know when does it finish loading. I tried jQuery's document ready function but it seems to deploy sooner than the javascript file. I am able to access the variable like this though:
setTimeout("console.log(swifttagdiv.firstChild.firstChild.src)", 5000);
but this is just a test to see if the variable is global. Any ideas?
You can inject the script by using Javascript instead of putting it in your page. This way you can control when it is loaded.
Here's a function I use to inject code in pages dynamically:
function inject(src, cb, target){
target = target || document.body;
var s = document.createElement('SCRIPT');
s.charset = 'UTF-8';
if(typeof cb === 'function'){
s.onload = function(){
cb(s);
};
s.onreadystatechange = function () {
(/loaded|complete/).test(s.readyState) && cb(s);
};
}
s.src = src;
target.appendChild(s);
return s;
}
to use it:
inject('/path/to/file.js', function(script){
//your code here
})
var checkvarint = setInterval(function(){
if(swifttagdiv.firstChild.firstChild.src){
varLoaded(); clearInterval(checkvarint);
}
},10);
function varLoaded(){
alert("LOADED!");
alert(swifttagdiv.firstChild.firstChild.src);
}
Mic's answer is great.
Here is the same thing only less abstracted, perhaps it will be instructive
var scriptElement = document.createElement( 'script' );
scriptElement.type = "text/javascript";
scriptElement.src = "https://ajax.googleapis.com/ajax/libs/ext-core/3.1.0/ext-core-debug.js";
function loadHandler() {
alert( 'loaded' );
}
// for ie
scriptElement.onreadystatechange = function () {
if( this.readyState == 'complete' ){
loadHandler();
}
}
// for others
scriptElement.onload= loadHandler;
document.getElementsByTagName('head')[0].appendChild( scriptElement );
Related
I am taking a page that does not have jquery referenced in the dom. I need to use some of the jquery functionality, so i figured the best bet is to inject jquery into the existing dom. I got the function idea from Load jQuery with Javascript and use jQuery and How to include jQuery dynamically in any website using pure javascript ....none of these solutions seem to work for me. I am still getting an error not recognizing the jquery selector.
(function() {
function getScript(url, success) {
var script = document.createElement('script');
script.src = url;
var head = document.getElementsByTagName('head')[0],
done = false;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function() {
if (!done && (!this.readyState
|| this.readyState == 'loaded'
|| this.readyState == 'complete')) {
done = true;
success();
script.onload = script.onreadystatechange = null;
head.removeChild(script);
}
};
head.appendChild(script);
}
getScript('https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js',function() {
// Yay jQuery is ready \o/
});
cards = $('div:first');
$('body').empty().append(cards);
// Delete first and second child divs
first = $('div:first div:first');
$('div:first div:first').css('position', '').css('left', '').css('z-index', '').css('height', '250px').remove();
second = $('div:first div:first');
$('div:first div:first').css('position', 'relative').css('left', '').css('z-index', '').remove();
$('body').empty().append(first).append(second);
})();
You're supposed to move your own code into the callback, to where it says // Yay.... Only then is your code run after jQuery was loaded.
getScript('https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js', function() {
// Yay jQuery is ready \o/
$('body').empty().append($('div:first'));
// Delete first and second child divs
for (var i = 0; i < 2; i++) {
$('div:first div:first').remove();
}
});
I am using Google API, based on their link I have to put the following script in the HTML file
<script src="https://apis.google.com/js/client.js?onload=callback"></script>
The custom callback function is being loaded after the client.js is loaded successfully.
function callback() {
var ROOT = 'https://your_app_id.appspot.com/_ah/api';
gapi.client.load('your_api_name', 'v1', function() {
doSomethingAfterLoading();
}, ROOT);
}
I would like to
Separate HTML with JS file
I downloaded the client.js file and put it in my local repo. But for reducing web request I would like to concat the client.js with other JS file. But I have no idea how to load the content with the concatenated JS file with the callback is being called
Thanks in advance
If you are looking for javascript only solution:
var sScriptSrc = "https://apis.google.com/js/client.js?onload=callback"
loadScript(sScriptSrc);
function loadScript(sScriptSrc) {
var oHead = document.getElementsByTagName("HEAD")[0];
var oScript = document.createElement('script');
oScript.type = 'text/javascript';
oScript.src = sScriptSrc;
oHead.appendChild(oScript);
oScript.onload = loadedCallback();
}
function loadedCallback() {
alert("WoHooo I am loaded");
}
See it running here: JSFiddle
EDIT
Let me do some refining, if I understand correctly what you want to achieve:
I made a simple main html page:
<html>
<head>
<script src="client.js"></script>
</head>
<body>
PAGE BODY
</body>
</html>
Which is loading client.js
client.js contains:
// you can call this function with
// param1: src of the script to load
// param2: function name to be executed once the load is finished
function loadScript(sScriptSrc, loadedCallback) {
var oHead = document.getElementsByTagName("HEAD")[0];
var oScript = document.createElement('script');
oScript.type = 'text/javascript';
oScript.src = sScriptSrc;
oHead.appendChild(oScript);
oScript.onload = loadedCallback;
}
// let's load the Google API js and run function GoggleApiLoaded once it is done.
loadScript("https://apis.google.com/js/client.js", GoggleApiLoaded);
function GoggleApiLoaded() {
alert("WoHooo Google API js loaded");
}
Of course, instead of GoggleApiLoaded example function you could run a method which start the loading of different js and the callback of that one could load a next one and so on...
Is this what you were looking for?
jQuery has a nice method for this. https://api.jquery.com/jquery.getscript/
jQuery.getScript("https://apis.google.com/js/client.js", function() {
console.log("hello");
})
If you want to be compatible with IE, including IE 9, you can use this async JS file loader & callback:
function loadAsync(src, callback){
var script = document.createElement('script');
script.src = src;
script.type = 'text/javascript';
script.async = true;
if(callback != null){
if (script.readyState) { // IE, incl. IE9
script.onreadystatechange = function() {
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
};
} else {
script.onload = function() { // Other browsers
callback();
};
}
}
a=document.getElementsByTagName('script')[0];
a.parentNode.insertBefore(script,a);
}
loadAsync("https://www.example.com/script.js", callbackFunction);
function callbackFunction() {
console.log('Callback function run');
}
I have a initializor.js that contains the following:
if(typeof jQuery=='undefined')
{
var headTag = document.getElementsByTagName("head")[0];
var jqTag = document.createElement('script');
jqTag.type = 'text/javascript';
jqTag.src = 'jquery.js';
headTag.appendChild(jqTag);
}
I am then including that file somewhere on another page. The code checks if jQuery is loaded, and if it isn't, adds it to the Head tag.
However, jQuery is not initializing, because in my main document, I have a few events declared just to test this. I also tried writing some jQuery code below the check, and Firebug said:
"jQuery is undefined".
Is there a way to do this? Firebug shows the jquery inclusion tag within the head tag!
Also, can I dynamically add code into the $(document).ready() event? Or wouldn't it be necessary just to add some Click events to a few elements?
jQuery is not available immediately as you are loading it asynchronously (by appending it to the <head>). You would have to add an onload listener to the script (jqTag) to detect when it loads and then run your code.
e.g.
function myJQueryCode() {
//Do stuff with jQuery
}
if(typeof jQuery=='undefined') {
var headTag = document.getElementsByTagName("head")[0];
var jqTag = document.createElement('script');
jqTag.type = 'text/javascript';
jqTag.src = 'jquery.js';
jqTag.onload = myJQueryCode;
headTag.appendChild(jqTag);
} else {
myJQueryCode();
}
To include jQuery you should use this:
<script src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="jquery.js">\x3C/script>')</script>
it uses the Google CDN but provides a fallback an has a protocol relative URL.
Note: Be sure to change the version number to the latest version
if window.jQuery is defined, it will not continue to read the line since it is an or that already contains a true value, if not it wil (document.)write the value
see: theHTML5Boilerplate
also: you forgot the quotes, if jQuery is not defined:
typeof window.jQuery === "undefined" //true
typeof window.jQuery == undefined //false ,this is wrong
you could also:
window.jQuery === undefined //true
If you're in an async function, you could use await like this:
if(!window.jQuery){
let script = document.createElement('script');
document.head.appendChild(script);
script.type = 'text/javascript';
script.src = "//ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js";
await script.onload
}
/* Your jQuery code here */
If you're not, you can use (async function(){/*all the code*/})() to wrap and run all the code inside one
.
Alternatively, refactoring Adam Heath's answer (this is more readable IMO). Bottom line, you need to run the jQuery code AFTER jQuery finished loading.
jQueryCode = function(){
// your jQuery code
}
if(window.jQuery) jQueryCode();
else{
var script = document.createElement('script');
document.head.appendChild(script);
script.type = 'text/javascript';
script.src = "//ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js";
script.onload = jQueryCode;
}
Or you could also wrap it in a function to change the order of the code
function runWithJQuery(jQueryCode){
if(window.jQuery) jQueryCode();
else{
var script = document.createElement('script');
document.head.appendChild(script);
script.type = 'text/javascript';
script.src = "//ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js";
script.onload = jQueryCode;
}
}
runWithJQuery(function jQueryCode(){
// your jQuery code
})
The YepNope loader can be used to conditionally load scripts, has quite a nice, easy to read syntax, they have an example of just this on their website.
You can get it from their website.
Example taken from their website:
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');
}
}
}
This site code is solved my problem.
function loadjQuery(url, success){
var script = document.createElement('script');
script.src = url;
var head = document.getElementsByTagName('head')[0],
done = false;
head.appendChild(script);
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function() {
if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
done = true;
success();
script.onload = script.onreadystatechange = null;
head.removeChild(script);
}
};
}
if (typeof jQuery == 'undefined'){
loadjQuery('http://code.jquery.com/jquery-1.10.2.min.js', function() {
// Write your jQuery Code
});
} else {
// jQuery was already loaded
// Write your jQuery Code
}
http://99webtools.com/blog/load-jquery-if-not-already-loaded/
This is old post but I create one workable solution tested on various places.
Here is the code.
<script type="text/javascript">
(function(url, position, callback){
// default values
url = url || 'https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js';
position = position || 0;
// Check is jQuery exists
if (!window.jQuery) {
// Initialize <head>
var head = document.getElementsByTagName('head')[0];
// Create <script> element
var script = document.createElement("script");
// Append URL
script.src = url;
// Append type
script.type = 'text/javascript';
// Append script to <head>
head.appendChild(script);
// Move script on proper position
head.insertBefore(script,head.childNodes[position]);
script.onload = function(){
if(typeof callback == 'function') {
callback(jQuery);
}
};
} else {
if(typeof callback == 'function') {
callback(jQuery);
}
}
}('https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js', 5, function($){
console.log($);
}));
</script>
Explanation you can find HERE.
I know that IE doesn't have a load event for <script> elements — is there any way to make up for that reliably?
I've seen some talk of things (e.g., requestState == "complete") but nothing very verifiable.
This is to be used so that code can be called after a script is finished loading, so that I don't have to use AJAX to load new sources (thus eliminating issues with cross-domain AJAX).
You can use a script loader like head.js. It has its own load callback and it will decrease load time too.
From the headjs code: (slightly modified to be more portable)
function scriptTag(src, callback) {
var s = document.createElement('script');
s.type = 'text/' + (src.type || 'javascript');
s.src = src.src || src;
s.async = false;
s.onreadystatechange = s.onload = function () {
var state = s.readyState;
if (!callback.done && (!state || /loaded|complete/.test(state))) {
callback.done = true;
callback();
}
};
// use body if available. more safe in IE
(document.body || head).appendChild(s);
}
I want to add that if you don't support IE7 and below, you don't need onreadystatechange stuff. Source: quircksmode.org
Simplified and working code from original answer:
function loadScript(src, callback) {
var s = document.createElement('script');
s.type = 'text/javascript';
s.src = src;
s.async = false;
if(callback) {
s.onload = callback;
}
document.body.appendChild(s);
}
This is just an extension of ilia's answer. I used scriptTag like this: Works great:
// these 3 scripts load serially.
scriptTag(boot_config.DEPENDENCIES.jquery,function(){
// jquery ready - set a flag
scriptTag(boot_config.DEPENDENCIES.jqueryui,function(){
// jqueryui ready - set a flag
scriptTag(boot_config.DEPENDENCIES.your_app,function(){
// your_app is ready! - set a flag
});
});
});
// these 2 scripts load in paralell to the group above
scriptTag(boot_config.EXTERNALS.crypto,function(){
// crypto ready - set a flag
});
scriptTag(boot_config.EXTERNALS.cropper,function(){
// cropper ready - set a flag
});
Problem:
Load js files asynchronously, then check to see if the dom is loaded before the callback from loading the files is executed.
edit: We do not use jQuery; we use Prototype.
edit: added more comments to the code example.
I am trying to load all of my js files asynchronously so as to keep them from blocking the rest of the page. But when the scripts load and the callback is called, I need to know if the DOM has been loaded or not, so I know how to structure the callback. See below:
//load asynchronously
(function(){
var e = document.createElement('script');
e.type = "text/javascript";
e.async = true;
e.src = srcstr;
// a little magic to make the callback happen
if(navigator.userAgent.indexOf("Opera")){
e.text = "initPage();";
}else if(navigator.userAgent.indexOf("MSIE")){
e.onreadystatechange = initPage;
}else{
e.innerHTML = "initPage();";
}
// attach the file to the document
document.getElementsByTagName('head')[0].appendChild(e);
})();
initPageHelper = function(){
//requires DOM be loaded
}
initPage = function(){
if(domLoaded){ // if dom is already loaded, just call the function
initPageHelper();
}else{ //if dom is not loaded, attach the function to be run when it does load
document.observe("dom:loaded", initPageHelper);
}
}
The callback gets called properly due to some magic behind the scenes that you can learn about from this Google talk: http://www.youtube.com/watch?v=52gL93S3usU&feature=related
What's the easiest, cross-browser method for asking if the DOM has loaded already?
EDIT
Here's the full solution I went with.
I included prototype and the asynchronous script loader using the normal method. Life is just so much easier with prototype, so I'm willing to block for that script.
<script type="text/javascript" src="prototype/prototype.js"></script>
<script type="text/javascript" src="asyncLoader.js"></script>
And actually, in my code I minified the two files above and put them together into one file to minimize transfer time and http requests.
Then I define what I want to run when the DOM loads, and then call the function to load the other scripts.
<script type="text/javascript">
initPage = function(){
...
}
</script>
<script type="text/javascript">
loadScriptAsync("scriptaculous/scriptaculous.js", initPage);
loadScriptAsync("scriptaculous/effects.js", initPage);
loadScriptAsync("scriptaculous/controls.js", initPage);
...
loadScriptAsync("mypage.js", initPage);
</script>
Likewise, the requests above are actually compressed into one httpRequest using a minifier. They are left separate here for readability. There is a snippet at the bottom of this post showing what the code looks like with the minifier.
The code for asyncLoader.js is the following:
/**
* Allows you to load js files asynchronously, with a callback that can be
* called immediately after the script loads, OR after the script loads and
* after the DOM is loaded.
*
* Prototype.js must be loaded first.
*
* For best results, create a regular script tag that calls a minified, combined
* file that contains Prototype.js, and this file. Then all subsequent scripts
* should be loaded using this function.
*
*/
var onload_queue = [];
var dom_loaded = false;
function loadScriptAsync(src, callback, run_immediately) {
var script = document.createElement('script');
script.type = "text/javascript";
script.async = true;
script.src = src;
if("undefined" != typeof callback){
script.onload = function() {
if (dom_loaded || run_immediately)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
};
script.onreadystatechange = function() {
if (script.readyState == 'complete'){
if (dom_loaded || run_immediately)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
}else if(script.readyState == 'loaded'){
eval(script);
if (dom_loaded || run_immediately)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
}
};
}
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
}
document.observe("dom:loaded", function(){
dom_loaded = true;
var len = onload_queue.length;
for (var i = 0; i < len; i++) {
onload_queue[i]();
}
onload_queue = null;
});
I added the option to run a script immediately, if you have scripts that don't rely on the page DOM being fully loaded.
The minified requests actually look like:
<script type="text/javascript" src="/min/?b=javascript/lib&f=prototype/prototype.js,asyncLoader.js"></script>
<script type="text/javascript"> initPage = function(e){...}</script>
<script type="text/javascript">
srcstr = "/min/?f=<?=implode(',', $js_files)?>";
loadScriptAsync(srcstr, initPage);
</script>
They are using the plugin from: [http://code.google.com/p/minify/][1]
What you need is a simple queue of onload functions. Also please avoid browser sniffing as it is unstable and not future proof. For full source code see the [Demo]
var onload_queue = [];
var dom_loaded = false;
function loadScriptAsync(src, callback) {
var script = document.createElement('script');
script.type = "text/javascript";
script.async = true;
script.src = src;
script.onload = script.onreadystatechange = function() {
if (dom_loaded)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
};
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
}
function domLoaded() {
dom_loaded = true;
var len = onload_queue.length;
for (var i = 0; i < len; i++) {
onload_queue[i]();
}
onload_queue = null;
};
// Dean's dom:loaded code goes here
// do stuff
domLoaded();
Test usage
loadScriptAsync(
"http://code.jquery.com/jquery-1.4.4.js",
function() {
alert("script has been loaded");
}
);
You can always put your initial loader script at the bottom, right before the closing body tag.