$(document).ready() source
I need to wait for document readyness in my JavaScript, to insert a div at the bottom of the body.
I want to:
- make this JavaScript file as small as possible (compile it down to < 1kb if possible)
- inline the code that provides the document readyness in a closure (without exporting it)
Inlining the whole jQuery source in my file would be too big, so I'm looking for other methods. window.onload
would work, but I specifically want document readyness, and not wait for the window.onload
event.
Does anyone know a JS snippet that can do this? Or should I just copy part of jQuery's source?
EDIT:
I managed to crawl the jQuery source and put together with the following snippet:
var ready = (function () {
var ready_event_fired = false;
var ready_event_listener = function (fn) {
// Create an idempotent version of the 'fn' function
var idempotent_fn = function () {
if (ready_event_fired) {
return;
}
ready_event_fired = true;
return fn();
}
// The DOM ready check for Internet Explorer
var do_scroll_check = function () {
if (ready_event_fired) {
return;
}
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
try {
document.documentElement.doScroll('left');
} catch(e) {
setTimeout(do_scroll_check, 1);
return;
}
// Execute any waiting functions
return idempotent_fn();
}
// If the browser ready event has already occured
if (document.readyState === "complete") {
return idempotent_fn()
}
// Mozilla, Opera and webkit nightlies currently support this event
if (document.addEventListener) {
// Use the handy event callback
document.addEventListener("DOMContentLoaded", idempotent_fn, false);
// A fallback to window.onload, that will always work
window.addEventListener("load", idempotent_fn, false);
// If IE event model is used
} else if (document.attachEvent) {
// ensure firing before onload; maybe late but safe also for iframes
docu开发者_StackOverflow社区ment.attachEvent("onreadystatechange", idempotent_fn);
// A fallback to window.onload, that will always work
window.attachEvent("onload", idempotent_fn);
// If IE and not a frame: continually check to see if the document is ready
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch (e) {}
if (document.documentElement.doScroll && toplevel) {
return do_scroll_check();
}
}
};
return ready_event_listener;
})();
// TEST
var ready_1 = function () {
alert("ready 1");
};
var ready_2 = function () {
alert("ready 2");
};
ready(function () {
ready_1();
ready_2();
});
Thank you very much for helping me find this in the jQuery source. I can now put all this in a closure and do my work without exporting any functions and polluting the global scope.
One option would be to just get the core.js
jQuery file from github.
You could probably slim it down quite a bit for code you don't need. Then run it through YUI compressor, and it should be pretty small.
- http://github.com/jquery/jquery/blob/1.4.2/src/core.js (jQuery core)
- http://yui.2clics.net/ (YUI compressor online)
I tried it, and this code worked properly:
$(function() {
var newDiv = document.createElement('div');
document.getElementsByTagName('body')[0].appendChild(newDiv);
});
Update: This was as small as I got it. It is entirely from jQuery
and is around 1,278 bytes (compressed). Should get smaller when you gzip.
Only difference is that you need to call it like:
$.fn.ready(function() {
// your code
});
YUI Compressed:
(function(){var e=function(i,j){},c=window.jQuery,h=window.$,d,g=false,f=[],b;e.fn={ready:function(i){e.bindReady();if(e.isReady){i.call(document,e)}else{if(f){f.push(i)}}return this}};e.isReady=false;e.ready=function(){if(!e.isReady){if(!document.body){return setTimeout(e.ready,13)}e.isReady=true;if(f){var k,j=0;while((k=f[j++])){k.call(document,e)}f=null}if(e.fn.triggerHandler){e(document).triggerHandler("ready")}}};e.bindReady=function(){if(g){return}g=true;if(document.readyState==="complete"){return e.ready()}if(document.addEventListener){document.addEventListener("DOMContentLoaded",b,false);window.addEventListener("load",e.ready,false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",b);window.attachEvent("onload",e.ready);var i=false;try{i=window.frameElement==null}catch(j){}if(document.documentElement.doScroll&&i){a()}}}};d=e(document);if(document.addEventListener){b=function(){document.removeEventListener("DOMContentLoaded",b,false);e.ready()}}else{if(document.attachEvent){b=function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",b);e.ready()}}}}function a(){if(e.isReady){return}try{document.documentElement.doScroll("left")}catch(i){setTimeout(a,1);return}e.ready()}window.jQuery=window.$=e})();
Full source (again, this is jQuery
code):
(function() {
var jQuery = function( selector, context ) {
},
_jQuery = window.jQuery,
_$ = window.$,
rootjQuery,
readyBound = false,
readyList = [],
DOMContentLoaded;
jQuery.fn = {
ready: function( fn ) {
jQuery.bindReady();
if ( jQuery.isReady ) {
fn.call( document, jQuery );
} else if ( readyList ) {
readyList.push( fn );
}
return this;
}
};
jQuery.isReady = false;
jQuery.ready = function() {
if ( !jQuery.isReady ) {
if ( !document.body ) {
return setTimeout( jQuery.ready, 13 );
}
jQuery.isReady = true;
if ( readyList ) {
var fn, i = 0;
while ( (fn = readyList[ i++ ]) ) {
fn.call( document, jQuery );
}
readyList = null;
}
if ( jQuery.fn.triggerHandler ) {
jQuery( document ).triggerHandler( "ready" );
}
}
};
jQuery.bindReady = function() {
if ( readyBound ) {
return;
}
readyBound = true;
if ( document.readyState === "complete" ) {
return jQuery.ready();
}
if ( document.addEventListener ) {
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
window.addEventListener( "load", jQuery.ready, false );
} else if ( document.attachEvent ) {
document.attachEvent("onreadystatechange", DOMContentLoaded);
window.attachEvent( "onload", jQuery.ready );
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch(e) {}
if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
};
rootjQuery = jQuery(document);
if ( document.addEventListener ) {
DOMContentLoaded = function() {
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
jQuery.ready();
};
} else if ( document.attachEvent ) {
DOMContentLoaded = function() {
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", DOMContentLoaded );
jQuery.ready();
}
};
}
function doScrollCheck() {
if ( jQuery.isReady ) {
return;
}
try {
document.documentElement.doScroll("left");
} catch(e) {
setTimeout( doScrollCheck, 1 );
return;
}
jQuery.ready();
}
window.jQuery = window.$ = jQuery;
})();
I'm sure there are more bytes that could be removed.
Don't forget:
/*!
* jQuery JavaScript Library v1.4.2
* http://jquery.com/
*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
There are several implementations for "DOMReady" functions but most that I can find seem a bit dated, so I don't know how they will behave with IE8 and such.
I would recommend using jQuery's ready() as I think it promises the most cross-browser compatibility. I'm not an expert in jQuery's source code, but this seems to be the right spot (lines 812-845 or search for function bindReady
).
You can start with script: http://snipplr.com/view/6029/domreadyjs/, not optimized (but work) for latest Safari though (e.g. use timer instead of supported DOMContentLoaded).
精彩评论