Recently I am writing a Javascript framework. I just encapsulated the DOMContentLoaded event, and I was a little excited. I took notes on the principles and compatibility issues encountered during the development process, so as to avoid forgetting everywhere.
When we write js code, we usually add window.onload event, mainly to use getElementById, getElementsByTagName and other methods to select DOM elements for operation after the DOM is loaded. However, window.load will wait until the DOM, script, CSS is loaded, and all resources in the image or even iframe are triggered. In many cases, the web page has more pictures and is larger. It takes a long time to load the image, and it is obviously too late to execute js, which will often affect the user experience.
Many js frameworks have a document.ready function, such as JQuery's $(document).ready() method, which can execute js code immediately after the DOM is loaded, so that the image can be loaded slowly.
The core of document.ready is the DOMContentLoaded event. Firefox, chrome, opera, safari, and ie9+ can all use addEventListener('DOMContentLoaded',fn,false) for event binding. ie6~8 does not support DOMContentLoaded event, so compatibility processing should be performed for ie6~8.
The information says that ie6~8 can use the document.onreadystatechange event to listen to whether the document.readyState status is equal to complete to determine whether the DOM has been loaded. If an iframe is embedded in the page, the document.readyState of ie6~8 will wait until all resources in the iframe are loaded before they become complete. At this time, the iframe becomes a time-consuming major user. But after testing, even if there is no iframe in the page, when readyState equals complete, the onload event is actually triggered instead of the DOMContentLoaded event, which is surprising to this point.
Fortunately, ie has a unique doScroll method. When the page DOM is not loaded, an error will be reported when the doScroll method is called. Conversely, as long as doScroll is called at intervals until no error is reported, it means that the page DOM has been loaded. This method is valid regardless of whether the content in the picture and iframe has been loaded.
If multiple js files are bound to the document.ready event, in order to prevent the browser from repeatedly binding and performing in an orderly manner, an event queue mechanism can be introduced to solve the problem.
The above is the principle and compatibility issue of the document.ready event. The following is a paragraph of example code. In order to facilitate understanding of the execution process, the execution process is written in the comments using the function encapsulation mode. If there is any inappropriateness, please give me some advice.
The code copy is as follows:
//Save the event queue of domReady
eventQueue = [];
//Judge whether the DOM has been loaded
isReady = false;
//Judge whether DOMReady is bound
isBind = false;
/*Execute domReady()
*
*@param {function}
*@execute pushes the event handler into the event queue and binds DOMContentLoaded
* If the DOM loading has been completed, execute immediately
*@caller
*/
function domReady(fn){
if (isReady) {
fn.call(window);
}
else{
eventQueue.push(fn);
};
bindReady();
};
/*domReady event binding
*
*@param null
*@execute Modern browsers bind DOMContentLoaded through addEvListener, including ie9+
ie6-8 determines whether the DOM has been loaded by judging doScroll
*@caller domReady()
*/
function bindReady(){
if (isReady) return;
if (isBind) return;
isBind = true;
if (window.addEventListener) {
document.addEventListener('DOMContentLoaded',execFn,false);
}
else if (window.attachEvent) {
doScroll();
};
};
/*doScroll determines whether the DOM of ie6-8 has been loaded.
*
*@param null
*@execute doScroll determines whether the DOM is loading
*@caller bindReady()
*/
function doScroll(){
try{
document.documentElement.doScroll('left');
}
catch(error){
return setTimeout(doScroll,20);
};
execFn();
};
/*Execution event queue
*
*@param null
*@execute loop execution event handler in queue
*@caller bindReady()
*/
function execFn(){
if (!isReady) {
isReady = true;
for (var i = 0; i < eventQueue.length; i++) {
eventQueue[i].call(window);
};
eventQueue = [];
};
};
//js file 1
domReady(function(){
...
});
//js file 2
domReady(function(){
...
});
//Note that if it is loaded asynchronously, do not bind the domReady method, otherwise the function will not be executed.
//Because before the asynchronous loading js download, DOMContentLoaded has been fired, and the addEventListener cannot be listened to when executing
Test page: Two large images are loaded. Onload requires the image to be loaded before js can be executed. DOMContentLoaded only needs to wait until the DOM is loaded to execute js. You can open firebug to view the loading process. Remember to clean the browser cache before each test.