일부 주류 JS 프레임 워 크 에서 DOMReady 사건 의 실현 소결

14228 단어 DOMReady 사건
원래 window 의 onload 사건 을 자주 사용 하 는데 이 사건 의 실제 효 과 는 페이지 분석/DOM 트 리 가 완성 되 었 을 때 그림,스 크 립 트,스타일 시트,심지어 iframe 의 모든 자원 을 다운로드 한 후에 발생 합 니 다.이것 은 많은 실제 응용 에 있어 너무 늦 어서 사용자 체험 에 영향 을 미친다.이 문 제 를 해결 하기 위해 ff 에 DOMContentLoaded 방법 이 추가 되 었 습 니 다.onload 에 비해 이 방법 은 더 일찍 작 동 되 었 습 니 다.이 방법 은 페이지 의 DOM 콘 텐 츠 로 딩 이 완 료 된 후에 작 동 되 며 다른 자원 의 로 딩 을 기다 리 지 않 아 도 됩 니 다.웹 킷 엔진 은 버 전 525(Webkit nightly 1/2008:525+)부터 이 사건 을 도 입 했 으 며 Opera 에 도 이 방법 이 포함 되 어 있다.지금까지 주류 인 IE 는 추가 할 의사 가 없다.IE 에 없 지만 해결 방법 이 있 습 니 다.다음은 몇 가지 주류 프레임 워 크 가 이 사건 에 대한 호환성 버 전 실현 방안 을 비교 해 보 았 습 니 다.관련 된 프레임 워 크 는 Prototype jQeury moontools dojo yui ext 1,Prototype 실현 코드
 
(function() {
/* Support for the DOMContentLoaded event is based on work by Dan Webb,
Matthias Miller, Dean Edwards and John Resig. */
var timer;
function fireContentLoadedEvent() {
if (document.loaded) return;
if (timer) window.clearInterval(timer);
document.fire("dom:loaded");
document.loaded = true;
}
if (document.addEventListener) {
if (Prototype.Browser.WebKit) {
timer = window.setInterval(function() {
if (/loaded|complete/.test(document.readyState))
fireContentLoadedEvent();
}, 0);
Event.observe(window, "load", fireContentLoadedEvent);
} else {
document.addEventListener("DOMContentLoaded",
fireContentLoadedEvent, false);
}
} else {
document.write("<"+"script id=__onDOMContentLoaded defer src=//:><\/script>");
$("__onDOMContentLoaded").onreadystatechange = function() {
if (this.readyState == "complete") {
this.onreadystatechange = null;
fireContentLoadedEvent();
}
};
}
})();
실현 방향 은 다음 과 같 습 니 다.webkit 이 라면 document 의 ready State 속성 을 문의 합 니 다.이 속성의 값 이 loaded 또는 complete 이면 DOMContentLoaded 이 벤트 를 촉발 하여 이 이 벤트 를 window.onload 에 등록 합 니 다.FF 라면 DOMContentLoaded 이 벤트 를 직접 등록 합 니 다.IE 라면 document.write 를 사용 하여 페이지 에 script 요 소 를 추가 하고 defer 속성 을 설정 합 니 다.마지막 으로 이 스 크 립 트 의 로드 완 료 를 DOMContentLoaded 이벤트 로 실행 합 니 다.이 실현 방식 의 문 제 는 주로 두 가지 가 있 습 니 다.첫째,document.write 를 통 해 script 을 쓰 고 defer 를 설정 하 는 방법 은 페이지 에 iframe 을 포함 하 는 경우 iframe 안의 내용 을 불 러 온 후에 야 발생 합 니 다.이것 은 onload 와 큰 차이 가 없습니다.둘째,웹 킷 은 525 이상 버 전에 서 DOMContentLoaded 방법 을 도 입 했 기 때문에 이 버 전에 서 폴 링 을 통 해 더 이상 실현 할 필요 가 없고 최적화 할 수 있다.2.jQuery
 
function bindReady(){
if ( readyBound ) return;
readyBound = true;
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", function(){
document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
jQuery.ready();
}, false );
// If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent("onreadystatechange", function(){
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", arguments.callee );
jQuery.ready();
}
});
// If IE and not an iframe
// continually check to see if the document is ready
if ( document.documentElement.doScroll && typeof window.frameElement === "undefined" ) (function(){
if ( jQuery.isReady ) return;
try {
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
document.documentElement.doScroll("left");
} catch( error ) {
setTimeout( arguments.callee, 0 );
return;
}
// and execute any waiting functions
jQuery.ready();
})();
}
// A fallback to window.onload, that will always work
jQuery.event.add( window, "load", jQuery.ready );
}
실현 방향 은 다음 과 같다.Webkit 를 Firefox 와 동등 하 게 취급 하 는 것 은 모두 DOMContentLoaded 사건 을 직접 등록 하 는 것 이지 만 Webkit 는 525 이상 버 전에 서 도입 되 었 기 때문에 호환성 위험 이 존재 한다.IE 에 대해 서 는 먼저 document 의 onready statechange 사건 을 등록 하고 테스트 를 통 해 이 방식 은 window.onload 와 비슷 하 며 모든 자원 다운로드 가 끝 난 후에 야 실 행 됩 니 다.이후 IE 이 고 페이지 가 iframe 에 없 으 면 setTiemout 을 통 해 document Element 의 doScroll 방법 을 계속 호출 하고 호출 에 성공 할 때 까지 DOMContentLoaded jQuery 가 IE 에 대한 해결 방안 을 촉발 하 는 새로운 방법 을 사용 했다.이 방법 은 IE 에서 유래 한 것 이다.http://javascript.nwbox.com/IEContentLoaded/。 IE 에서 DOM 의 일부 방법 은 DOM 해석 이 완 료 된 후에 만 호출 할 수 있다 는 원리 입 니 다.doScroll 은 바로 이러한 방법 입 니 다.반대로 doScroll 을 호출 할 수 있 을 때 DOM 해석 이 완 료 될 때 prototype 의 document.write 에 비해 페이지 에 iframe 이 있 을 때 효력 을 잃 는 문 제 를 해결 할 수 있 습 니 다.또한 jQuery 는 페이지 가 iframe 에 있 을 때 이 방법 이 효력 을 잃 을 까 봐 코드 에서 판단 한 것 같 습 니 다.iframe 에 있 으 면 document 의 onready statechange 를 통 해 이 루어 집 니 다.그렇지 않 으 면 doScroll 을 통 해 이 루어 집 니 다.그러나 테스트 를 통 해 iframe 에서 도 doScroll 은 유효 하 다.3.Moontols
 
(function(){
var domready = function(){
if (Browser.loaded) return;
Browser.loaded = true;
window.fireEvent('domready');
document.fireEvent('domready');
};
if (Browser.Engine.trident){
var temp = document.createElement('div');
(function(){
($try(function(){
temp.doScroll('left');
return $(temp).inject(document.body).set('html', 'temp').dispose();
})) ? domready() : arguments.callee.delay(50);
})();
} else if (Browser.Engine.webkit && Browser.Engine.version < 525){
(function(){
(['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
})();
} else {
window.addEvent('load', domready);
document.addEvent('DOMContentLoaded', domready);
}
})();
실현 방향 은 다음 과 같다.IE 라면 doScroll 방법 으로 이 루어 진다.525 버 전보 다 작은 웹 키 트 라면 document.ready State 에 문의 하여 이 루어 집 니 다.다른(FF/Webkit 고 판/Opera)은 DOMContentLoaded 이벤트 Moontotools 의 실현 방안 인 prototype 과 jQeury 의 종합 체 를 직접 등록 하여 webkit 에 대해 버 전 판단 을 하면 이 방안 을 더욱 튼튼 하 게 만 들 수 있다.doScroll 의 실현 에 있어 서 jQuery 에 비해 div 요 소 를 새로 만 들 었 고 사용 이 끝 난 후에 소각 하 였 으 며,jQuery 는 document Element 의 doScroll 을 직접 사용 하여 검 측 하여 더욱 간단 하고 효율 적 입 니 다.4.Dojo
 
// START DOMContentLoaded
// Mozilla and Opera 9 expose the event we could use
if(document.addEventListener){
// NOTE:
// due to a threading issue in Firefox 2.0, we can't enable
// DOMContentLoaded on that platform. For more information, see:
// http://trac.dojotoolkit.org/ticket/1704
if(dojo.isOpera || dojo.isFF >= 3 || (dojo.isMoz && dojo.config.enableMozDomContentLoaded === true)){
document.addEventListener("DOMContentLoaded", dojo._loadInit, null);
}
// mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already.
// also used for Mozilla because of trac #1640
window.addEventListener("load", dojo._loadInit, null);
}
if(dojo.isAIR){
window.addEventListener("load", dojo._loadInit, null);
}else if(/(WebKit|khtml)/i.test(navigator.userAgent)){ // sniff
dojo._khtmlTimer = setInterval(function(){
if(/loaded|complete/.test(document.readyState)){
dojo._loadInit(); // call the onload handler
}
}, 10);
}
// END DOMContentLoaded
(function(){
var _w = window;
var _handleNodeEvent = function(/*String*/evtName, /*Function*/fp){
// summary:
// non-destructively adds the specified function to the node's
// evtName handler.
// evtName: should be in the form "onclick" for "onclick" handlers.
// Make sure you pass in the "on" part.
var oldHandler = _w[evtName] || function(){};
_w[evtName] = function(){
fp.apply(_w, arguments);
oldHandler.apply(_w, arguments);
};
};
if(dojo.isIE){
// for Internet Explorer. readyState will not be achieved on init
// call, but dojo doesn't need it however, we'll include it
// because we don't know if there are other functions added that
// might. Note that this has changed because the build process
// strips all comments -- including conditional ones.
if(!dojo.config.afterOnLoad){
document.write('<scr'+'ipt defer="" src="//:" +="" onreadystatechange="if(this.readyState==\'complete\'){' + dojo._scopeName + '._loadInit();}">'
+ '</scr'+'ipt>'
);
}
try{
document.namespaces.add("v","urn:schemas-microsoft-com:vml");
document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");
}catch(e){}
}
// FIXME: dojo.unloaded requires dojo scope, so using anon function wrapper.
_handleNodeEvent("onbeforeunload", function() {
dojo.unloaded();
});
_handleNodeEvent("onunload", function() {
dojo.windowUnloaded();
});
})();
실현 방향 은 다음 과 같다.Opera 또는 FF3 이상 버 전이 라면 DOMContentLoaded<이벤트 에 직접 등록 하여 보험 을 들 고 window.onload 이벤트 도 등록 했다.웹 키 트 에 대해 서 는 document.ready State 에 문의 하여 이 루어 집 니 다.Air 라면 widnow.onload 이벤트 만 등록 합 니 다.IE 라면 defer 속성 이 있 는 script 을 페이지 에 쓰 고 onready statechange 이 벤트 를 등록 합 니 다.Dojo 가 IE 에서 구현 하 는 방안 역시 iframe 문 제 를 해결 할 수 없습니다.FF2 에서 매우 이상 한 bug 가 있 기 때문에 기본적으로 FF3 이상 버 전에 서 만 DOMContentLoaded 이 벤트 를 사용 하고-dojo.config.enable Moz Dom ContentLoaded 를 설정 합 니 다.FF 에서 이 설정 을 true 로 설정 하면 DOMContentLoaded 를 사용 합 니 다.이 점 은 유연성 을 충분히 고려 했다.웹 키 트 의 실현 에 있어 prototype 과 마찬가지 로 최 적 화 된 공간 이 있 습 니 다.5.YUI
 
(function() {
/*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller */
// Internet Explorer: use the readyState of a defered script.
// This isolates what appears to be a safe moment to manipulate
// the DOM prior to when the document's readyState suggests
// it is safe to do so.
if (EU.isIE) {
// Process onAvailable/onContentReady items when the
// DOM is ready.
YAHOO.util.Event.onDOMReady(
YAHOO.util.Event._tryPreloadAttach,
YAHOO.util.Event, true);
var n = document.createElement('p');
EU._dri = setInterval(function() {
try {
// throws an error if doc is not ready
n.doScroll('left');
clearInterval(EU._dri);
EU._dri = null;
EU._ready();
n = null;
} catch (ex) {
}
}, EU.POLL_INTERVAL);
// The document's readyState in Safari currently will
// change to loaded/complete before images are loaded.
} else if (EU.webkit && EU.webkit < 525) {
EU._dri = setInterval(function() {
var rs=document.readyState;
if ("loaded" == rs || "complete" == rs) {
clearInterval(EU._dri);
EU._dri = null;
EU._ready();
}
}, EU.POLL_INTERVAL);
// FireFox and Opera: These browsers provide a event for this
// moment. The latest WebKit releases now support this event.
} else {
EU._simpleAdd(document, "DOMContentLoaded", EU._ready);
}
/////////////////////////////////////////////////////////////
EU._simpleAdd(window, "load", EU._load);
EU._simpleAdd(window, "unload", EU._unload);
EU._tryPreloadAttach();
})();
실현 방향 은 Moontols 와 마찬가지 로 6.EXT
 
function initDocReady(){
var COMPLETE = "complete";
docReadyEvent = new Ext.util.Event();
if (Ext.isGecko || Ext.isOpera) {
DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
} else if (Ext.isIE){
DOC.write("<script id=" + IEDEFERED + " defer=defer src='/%27+%27/:'></script>");
DOC.getElementById(IEDEFERED).onreadystatechange = function(){
if(this.readyState == COMPLETE){
fireDocReady();
}
};
} else if (Ext.isWebKit){
docReadyProcId = setInterval(function(){
if(DOC.readyState == COMPLETE) {
fireDocReady();
}
}, 10);
}
// no matter what, make sure it fires on load
E.on(WINDOW, "load", fireDocReady);
}
실현 방향 과 Dojo 의 일치 로 더 이상 군말 하지 않 습 니 다.각 주류 의 구 조 를 정리 하고 다음 과 같은 버 전 을 썼 다.주로 가능 한 한 최적화 하고 FF2 의 Bug 를 고려 하여 DOMContentLoaded 를 사용 할 지 여부 의 스위치 설정 을 제공 합 니 다
 
/*
* DOMContentLoaded
* @param { Function } onready [ ] DOMContentLoaded
* @param { Object } config [ ]
*/
function onDOMContentLoaded(onready,config){
// , , 。
//var Browser = {};
// FF DOMContentLoaded( FF2 Bug)
this.conf = {
enableMozDOMReady:true
};
if( config )
for( var p in config)
this.conf[p] = config[p];
var isReady = false;
function doReady(){
if( isReady ) return;
// onready
isReady = true;
onready();
}
/*IE*/
if( Browser.ie ){
(function(){
if ( isReady ) return;
try {
document.documentElement.doScroll("left");
} catch( error ) {
setTimeout( arguments.callee, 0 );
return;
}
doReady();
})();
window.attachEvent('onload',doReady);
}
/*Webkit*/
else if (Browser.webkit && Browser.version < 525){
(function(){
if( isReady ) return;
if (/loaded|complete/.test(document.readyState))
doReady();
else
setTimeout( arguments.callee, 0 );
})();
window.addEventListener('load',doReady,false);
}
/*FF Opera webkit */
else{
if( !Browser.ff || Browser.version != 2 || this.conf.enableMozDOMReady)
document.addEventListener( "DOMContentLoaded", function(){
document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
doReady();
}, false );
window.addEventListener('load',doReady,false);
}
}

좋은 웹페이지 즐겨찾기