Load jQuery script after JavaScript script has finished - javascript

Similar to the way jQuery loads after the document has loaded I need a certain bit of jQuery after a JavaScript script has FINISHED.
I am loading contact data from Google Data API and the div where the data shows up needs to be completely loaded (by the JavaScript) to be accessible by the jQuery if I'm right.
Is there any way to do this?

Let's say you're waiting for Google Analytics to load. Now, you know that when Google Analytics loads, _gaq will be registered on the DOM. So, barring all other (better) asynchronous options, you can create a function that waits for the existence of _gaq:
waitForGaq = function(fn, attemptsLeft) {
var tick = attemptsLeft || 30;
if (typeof(_gaq) != 'object' || !_gaq._getAsyncTracker) {
//_gaq isn't registered yet
if (tick > 1) {
//recurse
setTimeout(function() {
waitForGaq(fn, tick - 1);
}, 100)
}
else {
//no ticks left, log error
log('failed to load window.gaq');
}
}
else {
//gaq is loaded, fire fn
fn();
}
}
So, any code needs to run AFTER _gaq is registered, you can add to the page thusly:
waitForGaq(function() {
//here's all my code...
});
To reiterate: recursing like this is only necessary if the script that you're adding doesn't offer an asynchronous way of interacting with the library. Google Analytics, for one, is kind of a moot example as it offers a cleaner approach:
http://code.google.com/apis/analytics/docs/tracking/asyncTracking.html

Try head.js
It allows to define exactly when (after specific script or after all of them have been loaded) you want to execute particular piece of code.
Not to mention it is by itself very neat.

inject a tag directly after the original with the jQuery code you need to run
<script src="foo" id="bar">
$("#bar").after(
$("<script>").text("some code")
);

if i understand correctly, you want to do something like:
<script>
// your javascript code
</script>
<script>
//your jquery code
</script>

Related

Using javascript to create Jquery link

We have a dynamic adbanner which is loaded onto websites via Google Double Click.
We use some Jquery in the code so as part of the set up we check if a website is running Jquery and if not we use Javascript to add a link to our Jquery file.
This is being done fine however I'm still getting an error "Uncaught ReferenceError: jQuery is not defined" I'd assume this is due to the order things are loading in but I'm not sure how to get around the issue. Everything works fine if you refresh the page the problem only seems to happen on first load.
Also if I open a new browser window and load the page a second time everything works fine.
Here's the code we are using to add the script tags to the head:
if(!window.jQuery)
{
var fm_j = document.createElement('script'); fm_j.type = 'text/javascript';
fm_j.src = 'js/jquery-1.8.3.min.js';
document.getElementsByTagName('head')[0].appendChild(fm_j);
}
This is a timing issue. When you load scripts dynamically like that, they are loaded asynchronously and don't block further execution of javascript on the page. This means that jQuery may not be loaded by the time your jQuery specific code is being run.
When you refresh, js/jquery-1.8.3.min.js has probably been cached and so will load faster, so you don't see errors.
The solution is to wrap your javascript into a function that is called once jQuery has loaded using a load event handler. Here's an example I adapted from this tutorial.
working example.
function getScript(success) {
var fm_j = document.createElement('script');
fm_j.type = 'text/javascript';
fm_j.src = 'js/jquery-1.8.3.min.js',
done = false;
// Attach handlers for all browsers
fm_j.onload = fm_j.onreadystatechange = function () {
if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
done = true;
// callback function provided as param
success();
};
};
document.getElementsByTagName('head')[0].appendChild(fm_j);
};
if(!window.jQuery) {
getScript(doSomething);
} else {
doSomething();
}
Also, as several commentors pointed out, many sites place banner ads inside iframes to keep the ads from "polluting" your page with their libraries.
iFrame could have been a better way to go #flyersun
It's simple and easy and effective, see here
http://support.microsoft.com/kb/272246

Page Initialization: Does one need onload?

Regarding this SO article and this SO article. I looked at these after noticing my web app does not fire in IE8...I don't care about backward compatibility at the moment but if it's one line of code why not? Anyways the other issue I was having is the onload event waits for all the content to load...so the user has no controls if he/she is waiting for images to download. This led me to believe that I should just use
<script type='text/javascript'>my_initialize_function()</script>
placed in the html where I want the page to initialize.
and say to bye to
window.onload = initializePage;
or
window.addEventListener('load',initialize_page);
or any similar.
My question is: Is this a valid approach to initializing one's page?
PS: I'm not using any libraries including JQuery...and obviously I would not try to initialize elements that have not been loaded yet.
No.
jQuery and similar libraries has an interesting approach. They simply capture different events in a crossbrowser manner while making it easier for the developer to use.
Let's consider the following code:
<script> document.getElementById('a').innerHTML='b'; </script>
<div id="a"></div>
It may or may not work depending on whether the browser runs javascript when it finds it or only after the whole document has been built.
On the other hand, if you used the proper event mechanism but the document has already been built, your code will not be called.
jQuery unites both paradigms to get a seamless and better system. Something like so:
var foo = {
// holds list of callbacks to call when document starts
stack: [],
// document not ready yet
ready: false,
// add callback to be called when document is ready
add: function(callback){
foo.stack.push(callback);
if(foo.ready)foo.fire(); // document is ready, call fire
},
// fire callbacks (document IS ready)
fire: function(){
for(var i=0; i<foo.stack.length; i++)
foo.stack[i]();
foo.stack = [];
foo.ready = true; // document IS ready
}
};
// bind to browser events
window.onload = foo.fire; // TODO: you should use actual events
// example of use
foo.add(function(){
alert('Document is ready!');
});
You could use jQuery's $(document).ready() event. It fires after the DOM is complete, but before all images are loaded.

Using a javascript library (underscore.js) within a class of my own

I'm trying to call a method from the underscore.js library within a class of my own. Something like this:
document.write('<scr'+'ipt type="text/javascript" src="Helpers/underscore-min.js" ></scr'+'ipt>');
function MyObject(object){
this.object = object;
this.RandomMethod = function(object)
{
var result = _.isEqual(object, this);
}
}
I am able to use it if I do it outside of the object declaration, but if it try to access it like this, the object does not exist.
Can somebody help?
The script you are adding has not had time to load yet since scripts are loaded asynchronously. To test this, try adding a timeout to delay the script execution:
document.write('<scr'+'ipt type="text/javascript" src="Helpers/underscore-min.js" ></scr'+'ipt>');
setTimeout(function() {
//your code here
}, 2000); //delay for 2 seconds to give script time to laod
This isn't a solution, just a way to verify the problem. If this is what's going on check out this article:
4 ways to dynamically load external JavaScript(with source)
You may want to employ the fourth option. It allows you to determine when the script has finished loading.

JavaScript: How to download JS asynchronously?

On my web site, I'm trying to accomplishes the fastest page load as possible.
I've noticed that it appears my JavaScript are not loading asynchronously. Picture linked below.
alt text http://img249.imageshack.us/img249/2452/jsasynch2.png
How my web site works is that it needs to load two external JavaScript files:
Google Maps v3 JavaScript, and
JQuery JavaScript
Then, I have inline JavaScript within the HTML that cannot be executed until those two files above are downloaded.
Once it loads these external javascript files, it then, and only then, can dynamically render the page. The reason why my page can't load until both Google Maps and JQuery are loaded is that - my page, based on the geolocation (using Gmaps) of the user will then display the page based on where they are located (e.g. New York, San Francisco, etc). Meaning, two people in different cities viewing my site will see different frontpages.
Question: How can I get my JavaScript files to download asynchronously so that my overall page load time is quickest?
UPDATE:
If I were to download, somehow, Google-maps and JQuery asynchronously, how would I create an event that would be fired once both Google-maps and JQuery have downloaded since my page has a hard dependency on those files to execute.
UPDATE 2
Even though there are 3 answers below, none still actually answer the problem I have. Any help would be greatly appreciated.
HTTP downloads are generally limited by browsers to two simultaneous downloads per domain. This is why some sites have the dynamic content on www.domain.tla and the images and javascript on static.domain.tla.
But browsers act slightly differently with scripts, while a script is downloading, however, the browser won't start any other downloads, even on different hostnames.
The standard solution is to move scripts to the bottom of the page, but there is a workaround that might or might not work for you: Insert the script DOM element using Javascript.
You could use something like this, which works pretty well in most browsers. It has some issues in IE6 at least, but I don't really have the time to investigate them.
var require = function (scripts, loadCallback) {
var length = scripts.length;
var first = document.getElementsByTagName("script")[0];
var parentNode = first.parentNode;
var loadedScripts = 0;
var script;
for (var i=0; i<length; i++) {
script = document.createElement("script");
script.async = true;
script.type = "text/javascript";
script.src = scripts[i];
script.onload = function () {
loadedScripts++;
if (loadedScripts === length) {
loadCallback();
}
};
script.onreadystatechange = function () {
if (script.readyState === "complete") {
loadedScripts++;
if (loadedScripts === length) {
loadCallback();
}
}
};
parentNode.insertBefore(script, first);
}
};
require([
"http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",
"http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js",
"http://ajax.googleapis.com/ajax/libs/yui/2.7.0/build/yuiloader/yuiloader-min.js"
], function () {
console.log(jQuery);
console.log($);
console.log(YAHOO);
});
Someone asked me to comment on this thread, but that was before #lonut posted a response. #lonut's code is a very good solution, but I have some comments (critical and not so critical):
First, #lonut's code assumes that the scripts do NOT have load dependencies on the other scripts. This is a little hard to explain, so let's work with the simple example of jquery.min.js and prototype.js. Suppose we have a simple page that just loads these two scripts like this:
<script src="jquery.min.js"></script>
<script src="prototype.js"></script>
Remember - there's nothing else in the page - no other JavaScript code. If you load that page the two scripts get downloaded and everything's fine. Now, what happens if you remove the jquery.min.js script? If you get errors from prototype.js because it's trying to reference symbols defined in jquery.min.js, then prototype.js has a load dependency on jquery.min.js - you cannot load prototype.js unless jquery.min.js has already been loaded. If, however, you don't get any errors, then the two scripts can be loaded in any order you wish. Assuming you have no load dependencies between your external scripts, #lonut's code is great. If you do have load dependencies - it gets very hard and you should read Chapter 4 in Even Faster Web Sites.
Second, one problem with #lonut's code is some versions of Opera will call loadCallback twice (once from the onload handler and a second time from the onreadystatechange handler). Just add a flag to make sure loadCallback is only called once.
Third, most browsers today open more than 2 connections per hostname. See Roundup on Parallel Connections.
The LABjs dynamic script loader is designed specifically for this type of case. For instance, you might do:
$LAB
.script("googlemaps.js")
.script("jquery.js")
.wait(function(){
// yay, both googlemaps and jquery have been loaded, so do something!
});
If the situation was a little more complex, and you had some scripts that had dependencies on each other, as Steve Souders has mentioned, then you might do:
$LAB
.script("jquery.js")
.wait() // make sure jquery is executed first
.script("plugin.jquery.js")
.script("googlemaps.js")
.wait(function(){
// all scripts are ready to go!
});
In either case, LABjs will download all of the scripts ("jquery.js", "googlemaps.js", and "plugin.jquery.js") in parallel, as least up to the point the browser will allow. But by judicious use of the .wait() in the chain, LABjs will make sure they execute in the proper order. That is, if there's no .wait() in between the two scripts in the chain, they will each execute ASAP (meaning indeterminate order between tehm). If there's a .wait() in between two scripts in the chain, then the first script will execute before the second script, even though they loaded in parallel.
Here is how I've managed to load gmaps asynchronously on a jquery mobile:
First, you can load jquery (i.e. with the require function posted above by Ionuț G. Stan)
Then you can make use of the callback param in gmaps to do the following:
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
var require = function (scripts, loadCallback) {
var length = scripts.length;
var first = document.getElementsByTagName("script")[0];
var parentNode = first.parentNode;
var loadedScripts = 0;
var script;
for (var i=0; i<length; i++) {
script = document.createElement("script");
script.async = true;
script.type = "text/javascript";
script.src = scripts[i];
script.onload = function () {
loadedScripts++;
if (loadedScripts === length) {
loadCallback();
}
};
script.onreadystatechange = function () {
if (script.readyState === "complete") {
loadedScripts++;
if (loadedScripts === length) {
loadCallback();
}
}
};
parentNode.insertBefore(script, first);
}
};
require([
"http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",], function () {
$.ajax({
type: "GET",
url: 'http://maps.googleapis.com/maps/api/js?v=3&sensor=false&callback=setMyMap',
dataType: "script"
});
});
function setMyMap() {
console.log('your actions here');
var coords = new google.maps.LatLng(40.5439532,-3.6441775);
var mOptions = {
zoom: 8,
center: coords,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("gmap"), mOptions);
}
</script>
<div id="gmap" style="width:299px; height:299px"></div>
</body>
The point is load jquery async (whathever method you choose) and on that callback place a new async call to gmaps with your starting method in the callback param of the gmaps script string.
Hope it helps
Regardless what order they download in, the scripts should be parsed/executed in the order in which they occur on the page (unless you use DEFER).
So, you can put both Google Maps first in the head, THEN JQuery. Then, in the body of your page somewhere:
<script language="Javascript">
function InitPage() {
// Do stuff that relies on JQuery and Google, since this script should
// not execute until both have already loaded.
}
$(InitPage); // this won't execute until JQuery is ready
</script>
But this does have the disadvantage of blocking your other connections while loading the beginning of the page, which isn't so awesome for page performance.
Instead, you can keep JQuery in the HEAD, but load the Google scripts from the InitPage() function, using JQuery's Javascript-loading functionality rather than the Google JSAPI. Then start your rendering when that call-back function executes. Same as the above, but with this InitPage() function instead:
function InitPage() {
$.getScript('Google Maps Javascript URL', function() {
// Safe to start rendering now
});
Move your javascript includes (<script src="...) from the HEAD element to the end of your BODY element. Generally whatever is placed in the HEAD is loaded synchronously, whatever is placed in the BODY is loaded asynchronously. This is more or less true for script includes, however most browsers these days block everything below the script until it is loaded - hence why having scripts included at the bottom of the body is best practice.
Here is the YUI guildline for this which explains it in further detail:
http://developer.yahoo.net/blog/archives/2007/07/high_performanc_5.html
This is also the reason why stylesheets should be in the head, and javascript should be in the body. As we do not want to see our page turn from spaghetti to niceness while the styles load asynchronously, and we don't want to wait on our javascript while our page loads.
The objective you have in mind would be served by using requireJS. RequireJS downloads the js resources asynchronously. Its a very simple and useful library to implement. Please read more here. http://requirejs.org/

loading javascript dependencies on demand

I'm sure there are different approaches to this problem, and I can think of some. But I'd like to hear other people's opinion on this. To be more specific I've built a widget that allows users to choose their location from a google maps map. This widget is displayed on demand and will probably be used every 1 out of 10 uses of the page where it's placed. The simplest way to load the dependency for this widget (google maps js api) is to place a script tag in the page. But this would make the browser request that script on every page load. I'm looking for a way to make the browser request that script only when the user requires for the widget to be displayed.
function loadJSInclude(scriptPath, callback)
{
var scriptNode = document.createElement('SCRIPT');
scriptNode.type = 'text/javascript';
scriptNode.src = scriptPath;
var headNode = document.getElementsByTagName('HEAD');
if (headNode[0] != null)
headNode[0].appendChild(scriptNode);
if (callback != null)
{
scriptNode.onreadystagechange = callback;
scriptNode.onload = callback;
}
}
And to use (I used swfObject as an example):
var callbackMethod = function ()
{
// Code to do after loading swfObject
}
// Include SWFObject if its needed
if (typeof(SWFObject) == 'undefined')
loadJSInclude('/js/swfObject.js', callbackMethod);
else
callbackMethod();
You might want to take a look at jsloader: http://www.jsloader.com/
Gaia Ajax does this (I know since I implemented it - I'm the original founder) and they're GPL. So unless you're afraid they'll sue you (they're FUDding me with lawsuits now) you might want to check out how they do it. Basic technology is to inject a script tag using DOM when script is needed. Though you must take care NOT to reference stuff in this file before it is finished loading (which happens asynchronously)
The solution to that problem (one solution) is to add up a variable at the bottom of the file and use recursive setTimeout calls to check if the variable is defined and defer execution of the code depending on the file being finished loading until that "bottom of JS file" variable is defined...
We actually also tracked which files where included by appending the hashed value of the filenames into a hidden field on the page. This means we never ended up including the same file more then once...
Pretty nifty in fact...
You might want to take a look at a real DEMO on real estate site.
On the demo page, just click on the link [Xem bản đồ] to see the map loaded on demand.
The map loaded only when the link be clicked not at the time of page load, so it can reduce page download time.
The Google AJAX APIs provide dynamic loading for Google's JavaScript APIs. There is an example of loading the Maps JS on-demand in the documentation:
function mapsLoaded() {
var map = new google.maps.Map2(document.getElementById("map"));
map.setCenter(new google.maps.LatLng(37.4419, -122.1419), 13);
}
function loadMaps() {
google.load("maps", "2", {"callback" : mapsLoaded});
}
you can load script dynamically by adding <script src="..."> tag to DOM tree.

Categories