Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
// HOWTO: load LABjs itself dynamically!
// inline this code in your page to load LABjs itself dynamically, if you're so inclined.
(function (global, oDOC, handler) {
var head = oDOC.head || oDOC.getElementsByTagName("head");
function LABjsLoaded() {
// do cool stuff with $LAB here
}
// loading code borrowed directly from LABjs itself
setTimeout(function () {
if ("item" in head) { // check if ref is still a live node list
if (!head[0]) { // append_to node not yet ready
setTimeout(arguments.callee, 25);
return;
}
head = head[0]; // reassign from live node list ref to pure node ref -- avoids nasty IE bug where changes to DOM invalidate live node lists
}
var scriptElem = oDOC.createElement("script"),
scriptdone = false;
scriptElem.onload = scriptElem.onreadystatechange = function () {
if ((scriptElem.readyState && scriptElem.readyState !== "complete" && scriptElem.readyState !== "loaded") || scriptdone) {
return false;
}
scriptElem.onload = scriptElem.onreadystatechange = null;
scriptdone = true;
LABjsLoaded();
};
scriptElem.src = "/path/to/LAB.js";
head.insertBefore(scriptElem, head.firstChild);
}, 0);
// required: shim for FF <= 3.5 not having document.readyState
if (oDOC.readyState == null && oDOC.addEventListener) {
oDOC.readyState = "loading";
oDOC.addEventListener("DOMContentLoaded", handler = function () {
oDOC.removeEventListener("DOMContentLoaded", handler, false);
oDOC.readyState = "complete";
}, false);
}
})(window, document);
// compressed more suitable for inlining
// ~640b before gzip
(function(g,b,d){var c=b.head||b.getElementsByTagName("head"),D="readyState",E="onreadystatechange",F="DOMContentLoaded",G="addEventListener",H=setTimeout;
function f(){
// $LAB stuff here
}
H(function(){if("item"in c){if(!c[0]){H(arguments.callee,25);return}c=c[0]}var a=b.createElement("script"),e=false;a.onload=a[E]=function(){if((a[D]&&a[D]!=="complete"&&a[D]!=="loaded")||e){return false}a.onload=a[E]=null;e=true;f()};
a.src="/path/to/LAB.js";
c.insertBefore(a,c.firstChild)},0);if(b[D]==null&&b[G]){b[D]="loading";b[G](F,d=function(){b.removeEventListener(F,d,false);b[D]="complete"},false)}})(this,document);
@dburry
Copy link

dburry commented Feb 4, 2011

My intention isn't to argue so hard that 2.2k should be more modular.... 2.2k is already very small, and the benefits of chopping it up further are therefore logically also probably small. My intention is to try to inspire people in general to think more modularly... Script loaders like LABjs can make the "overhead" of such modularity less of a problem.

I'm sorry that I opened this dialog with poking fun at a "loader to load a loader" and suggesting modularity to solve that... that seems to have sidetracked the main point I'm ultimately wanting to make :(

So my main point is: Please, everyone, use LABjs and other such script loader techniques to make your OTHER scripts (i.e. the ones that LABjs is loading) more modular! It can help save you a lot! LABjs is great, keep up the good work! And let's use it in the way that benefits us the most... with more modular code.

Secondary to this main point.... If anyone out there wants to pinch every byte and thinks LABjs itself is too big, and wants to use this "loader to load a loader" in this gist... then perhaps they should consider writing a more modular loader, since in theory that could be better than having duplication between the smaller and larger loaders.... Again, the benefits are probably very small, yes, since LABjs itself is so small, but we don't really know by how much for sure until we try it and measure it (maybe even no measurable benefit at all). Not worth it enough to bother even trying? well, yes, obviously that's been the case so far, since nobody's done it yet...

@getify
Copy link
Author

getify commented Feb 4, 2011

@dburry -- gotcha. perfectly valid points to be making. thanks. :)

Some really small loaders to consider:

@agibralter
Copy link

agibralter commented Feb 8, 2011

Does anyone know what "item" in head means?

@getify
Copy link
Author

getify commented Feb 8, 2011

("item" in head) is an expression which asks if head has a property called "item", which is testing to see if head is an actual DOM node or still just a placeholder DOMNodeList entry.

@agibralter
Copy link

agibralter commented Feb 8, 2011

Ah awesome, thank you.

One more question... what would happen if you added scriptElem.async = true into the mix?

@getify
Copy link
Author

getify commented Feb 8, 2011

pretty much nothing at this point. dynamic script elements have, for all intents and purposes, acted like "async=true" all along. The only exception being FF 3.6, where their behavior differed some. But starting in FF4, as well as upcoming Webkit (and hopefully Opera and IE soon), scripts will default to showing their property as "true" by default (and behaving like it).

So, the only real value to changing an async property value on a dynamic script element will be that you can change it from the default "true" set it to "false", which will cause it (and other scripts with async set to false) to still load in parallel, but to execute in correct (insertion) order (kinda like regular non-async script tags do in markup).

@agibralter
Copy link

agibralter commented Feb 8, 2011

I didn't realize that -- very interesting. Thanks again for the help!

@sajal
Copy link

sajal commented Mar 5, 2011

Wouldnt it be possible to make this snippet smaller/simpler?

Say i define a function such as LABjsLoaded() using this type of code
var LABjsLoaded = function(){
// cool $lab stuff here
};

Then load LABjs using something smaller like google analytics style snippet

(function() {
  var labjs = document.createElement('script');
  lab.src = '/path/to/lab.js';
  lab.setAttribute('async', 'true');
  document.documentElement.firstChild.appendChild(lab);
})();

The only difference would be that the base labjs file does a LABjsLoaded() after loading. (205 bytes before gzip)
Would be nice if labjs itself looked for the existence of a particular function, if it exists, call it once labjs is ready.

@getify
Copy link
Author

getify commented Mar 5, 2011

@sajal-
I am not a fan of that pattern/suggestion:

  1. it promotes having a global function not on the LABjs namespace, which is a bad practice to pollute the global namespace

  2. it would hardwire that the name of the function had to be exactly something as manually specificed in the LABjs source code, which makes it brittle if someone can't define a global function with that name for whatever reason (conflicts, etc).

  3. also, i still feel it's necessary to have the the document.readyState part of the snippet in there.

All in all, I think this snippet is pretty small as it is. And loading LAB.js itself dynamically is of questionable benefit anyway, because LAB.js is already itself so small, that a normal blocking script element load of it shouldn't cause any kind of noticeable delay.

Honestly, I recommend to people that instead of loading LABjs dynamically like this snippet does, instead to inline LABjs' source code into another file that they'd already be loading onto their page... For instance, on all my sites, what I do is have a single "load.js" file that I load at the bottom of the body, with a regular blocking script tag. Inside that "load.js" file, I have LABjs source code minified inline, and then I have my other page initialization code... for instance, I have my $LAB chains for what other scripts I want to load (like jQuery, etc). By having that all in one file, and loading it with a normal blocking script tag, it becomes basically like a "bootstrapper" for the page. As long as "load.js" stays decently small (<5-8k), it works pretty well.

@sajal
Copy link

sajal commented Mar 5, 2011

@getify: aha come to think of it it does make sense now to use the regular <script> tag for labjs.

Ive already working on a mechanism to bundle all self owned js including labjs into a single file, and all 3rd party js is handled by $LAB . No reason to not use <script> ... nothing more to block anyways...

I was getting a little too carried over trying to over-optimize.

@tobie
Copy link

tobie commented Jul 13, 2011

You probably mean: var head = oDOC.head || oDOC.getElementsByTagName("head")[0];

@getify
Copy link
Author

getify commented Jul 13, 2011

@tobie -- no, check lines 13-19 for how it does that

@tobie
Copy link

tobie commented Jul 13, 2011

Yuck.

@getify
Copy link
Author

getify commented Jul 13, 2011

@tobie -- it's to address an older IE quirk/bug where the [0] element is not yet set, and if you capture a ref to it too early, when it's made available, your ref will be invalid.

@tobie
Copy link

tobie commented Jul 13, 2011

Heh. Deserves a comment, don't you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment