$(document).ready() when jQuery is loaded as a dynamically appended <script> - javascript

For reasons that are beyond my control I have to load jQuery via a dynamically appended <script> tag, and only do this upon some arbitrary event, not at page load.
My problem is in detecting the moment when jquery is ready, for the code below doesn't work:
(function(){
var s=document.createElement('script');
s.setAttribute('type','text/javascript');
s.setAttribute('src','http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js');
document.getElementsByTagName('head')[0].appendChild(s);
})()
$(document).ready(function() {
// something that uses jquery and currently doesn't work
});
How do I detect the moment when jquery is ready to be used in this particular configuration?
Thanks in advance

Use the onload and onreadystatechange event handlers:
var scr = document.createElement('script'),
head = document.getElementsByTagName('head')[0];
scr.onload = scr.onreadystatechange = function(){
if( scr.readyState ){
if(scr.readyState === 'complete' || scr.readyState === 'loaded'){
scr.onreadystatechange = null;
myReadyFunc();
}
}
else{
myReadyFunc();
}
};
head.insertBefore(scr, head.firstChild);
function myReadyFunc() {
$(document).ready(function() {
// something that uses jquery and currently doesn't work
});
}

An old-school answer :)
You can create a recursive polling function that check to see if the $ object exists eg:
function poller(){
if($.length != 0){
//do what you want
}else{
setTimeout(poller, 100);
}
}
And right after you load the jQuery script run the poller function.

You can handle the <script> element's onreadystatechange event, and, if this.readyState is complete or loaded, run your function.
For Firefox, handle the onload event.
You can expose this in a wrapper function which takes a function as a parameter, and calls it if jQuery has been loaded, or puts it in an array (to call in the load handler) if it's not loaded.

Related

Is there a callback for element.innerHTML assignments?

When using Head.js, and setting the .src attribute of a script element, there is callback method that is called when the script is ready.
However, I wanted to load a script by assigning text to .innerHTML. When doing this the same callback did not fire when I updated/edited the code to use this property instead.
/*addScriptText1
** modified from head.js
**
**
*/
function addScriptText1(file_name, callback, key) {
var element = document.createElement('script');
element.async = true;
element.innerHTML = localStorage[file_name];
element.onreadystatechange = element.onload = function () {
$A.log('callback for element called');
if ((!element.readyState || /loaded|complete/.test(element.readyState))) {
localStorage[key + '_loaded'] = true;
callback();
}
};
document.head.appendChild(element);
}
Scripts are executed immediately when inline scripts are inserted into the DOM. This all happens synchronously, so you don't need a callback.
async has no effect here because you are not doing a network request. Similarly, the readyState is not useful because you are creating it programatically, so the script is going to be immediately loaded.
Using innerHTML on a script element is not supported cross-browser (including Firefox iirc). I would not recommend this approach. I would suggest eval or new Function instead.
Can scripts be inserted with innerHTML?

Run Two Functions (One In External File, One In Inline Javascript) On window.onload

I'm developing a website and I have an external JavaScript file that is linked to every page of the site which executes when the window.onload event is fired. The JavaScript executes fine on all pages which do not contain any inline JavaScript.
Any page that contains inline JavaScript also contains a JavaScript function which executes when the window.onload event is fired.
The problem I'm having is that the external JavaScript does not execute when window.onload is fired, only the internal JavaScript does. It appears as if the inline JavaScript function overwrites the function from the external JavaScript file.
I know that my external JavaScript file is first executed and then the inline JavaScript is executed. Is there anyway that I can execute both functions on window.onload?
How about changing the script that executes second to something like this:
var onload = function () {
// Do something
alert('Hello');
};
if(window.onload) {
// If a function is already bound to the onload event, execute that too.
var fn = window.onload;
window.onload = function () {
fn();
onload();
};
} else {
window.onload = onload;
}
Or use a library like jQuery which lets you do:
$(document).ready(function() {
alert('Hello');
}
This might be more of a work around than a proper fix but could you put your inline JS at the bottom of the html page so it is automatically called after the page is loaded. That will mean you don't have to actually use window.onload()
Two ways :
1 - Use addEventListener.
window.addEventListener("load", function(){ /*code*/ }, false);
2 - Use a hack like this:
var func = window.onload;
window.onload = function() {
if ( func ) func();
/* code */
};

Javascript onload event and other events before the document is ready

if (typeof $ == "undefined"){
console.log("$ is undefined. Adding javascript element to the document");
jQs = document.createElement("script");
jQs.type = 'text/javascript';
jQs.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(jQs, s);
// Run script once jQuery is loaded
console.log(" startScript() Start");
jQs.onload = startScript;
}
I have got the script above, which adds a script tag that points to jquery in the case if the $ is undefined. I try to insert the tag before the document begins and execute the startScript function. But what I think happens is that the body.onload doesnt trigger the startScript fast enough.. and that prevents its execution. Is there an event that would trigger teh startScript as soon as the jquery script tag / document is ready?
I think the answer lies in executing the startscript whenever the jquery tag is ready and the document is ready together..but i dont know how to achieve the effect..
Note that putting the code in $(document).ready() is an irrelevant solution because it would $ is undefined if there is no jquery file included in the script
note also that i dont want to execute the onload function on the jquery tag onload event,, cause it may mean that the function is being triggered several times
function init() {
if (typeof $ == "undefined"){
console.log("$ is undefined. Adding javascript element to the document");
jQs = document.createElement("script");
jQs.type = 'text/javascript';
jQs.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(jQs, s);
// Run script once jQuery is loaded
console.log(" startScript() Start");
jQs.onload = startScript;
}
}
window.onload = init;

Waiting on JS class load from dynamic script loading

I have a JS script which depends on jQuery.
I want to check for jQuery, and if it is not loaded/available add it myself, wait for it to load, and then define my script class.
The code I currently use:
// load jQuery if not loaded yet
if (typeof (jQuery) == 'undefined') {
var fileref = document.createElement('script');
fileref.setAttribute("type", "text/javascript");
fileref.setAttribute("src", 'http://code.jquery.com/jquery-1.4.4.min.js');
document.getElementsByTagName('body')[0].appendChild(fileref);
(ready = function() {
if ( typeof (jQuery) == 'undefined' || !jQuery) {
return setTimeout( ready, 1 );
} else {
// jQuery loaded and ready
jQuery.noConflict();
}
})();
}
// … class definition follows
var MView = function() …
Now, with FireFox 4 (I think it did work before, or execution was just too slow), it will continue the scripts execution even when I still want to wait on jQuery. The recursive setTimeout is non-blocking.
How can I fix this? Make setTimeout blocking? Use another approach? Is there a better way? A way at all?
The class should be global scope, so it can be used on the page that includes this script file.
I would recommend 2 things.
Use 'if (!jQuery)' since undefined is considered falsey
Use the script tag's onload event
if (!window.jQuery) {
var fileref = document.createElement('script');
fileref.setAttribute("type", "text/javascript");
fileref.setAttribute("src", 'http://code.jquery.com/jquery-1.4.4.min.js');
fileref.onload = function() {
// Callback code here
};
document.getElementsByTagName('body')[0].appendChild(fileref);
}

Execute javascript after external javascript document has loaded

I want to include a remote js file and then invoke a function once that has finished executing. I thought I could do something like this:
var sc = document.createElement('script');
sc.setAttribute('type', 'text/javascript');
sc.setAttribute('src', src);
sc.innerHTML = "alert('testing');"
parentNode.appendChild(sc);
Turns out, the alert('testing') gets wiped out be whatever is in the file. Is there anyway to do this?
This function will load library from scriptPath and execute passed handler function once script is loaded:
loadExternalScript : function(scriptPath, handler) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = scriptPath;
script.charset = 'utf-8';
script.onload = handler;
head.appendChild(script);
}
First thing is, forget about using src and inner contents on the same script tag. It doesn't work in any general way, although John Resig gave it some thought in this blog post.
Second thing is, decide whether you want to load the script synchronously or asynchronously. If the script is large or long-running, you'll either want to do it asynchronously, or do it synchronously at the bottom of the page so as not to block rendering.
Your approach (dynamically appending script tags) will load and run it asynchronously, which means the code that should run after it's finished needs to go in a callback that fires when the script is finished. Setting this up isn't very straightforward, so I'd suggest either using jQuery and its ajax.getScript function, or just copy the getScript functionality right out of the jQuery source (lines 3473-3505 of jQuery 1.3.2).
If you want to avoid all of that, just load it synchronously. This is done by using document.write. Your provided example would look like:
document.write("<scr" + "ipt src='" + src + "' type='text/javascript'></script>");
// The script is guaranteed to have executed at this point
alert('testing');
Be sure to keep "script" split up like that, I'm not sure why but it's a quirk of JavaScript.
Have you tried just creating a second script element containing the code you want to run and adding that after the you've added the one that needs downloading?
Adding another
<script></script>
section after the first one should work. AFAIK you can't mix external and inline JS in one tag.
However I'm not sure whether putting code into "innerHTML" will work as expected. I'm interested to see whether it does.
You might be able to use the sc load event to figure out when that script has loaded then do some action.
example http://iamnoah.blogspot.com/2008/01/ie-script-load-event.html
I created this script for myself yesterday. It uses jQuery to load JavaScript files via AJAX and adds them in a script tag to the header, and then calls a callback function I pass it.
Has been working fine for me.
/**
* Fetches and executes JavaScript files from the server.
* #param files A list of files to load, or a single filename as a string.
* #param callback The function to call when the process is done. Passes a boolean success value as the only parameter.
* #param thisObject The calling object for the callback.
*/
window.include = function(files, callback, thisObject) {
var current_location = null;
var recursive = false;
if(!(thisObject instanceof Object)) {
thisObject = window;
}
if(files instanceof Array || files instanceof Object) {
if(files.length > 0) {
current_location = files.shift();
recursive = true;
}
else {
callback.apply(thisObject, [true]);
return;
}
}
else if(typeof files == 'string') {
current_location = files;
}
else {
callback.apply(thisObject, [false]);
return;
}
if((current_location instanceof String || typeof current_location == 'string') && current_location != '')
{
$.ajax({
type : 'GET',
url : current_location,
timeout : 5000,
success : function(data) {
var scriptTag = $(document.createElement('script'));
scriptTag.attr('type', 'text/javascript');
scriptTag.html(data);
$('head').append(scriptTag);
if(recursive) {
window.adlib.include(files, callback, thisObject);
}
else {
callback.apply(thisObject, [true]);
}
},
error : function() {
callback.apply(thisObject, [false]);
}
});
}
}

Categories