深入理解Javascript封装DOMContentLoaded事件
最近在写一个Javascript的框架,刚把DOMContentLoaded事件封装好,略带小兴奋,把开发过程中遇到的原理和兼容性问题做篇笔记,省的忘记到处找。
我们在写js代码的时候,一般都会添加window.onload事件,主要是为了在DOM加载完后可以使用getElementById,getElementsByTagName等方法选取DOM元素进行操作,但是window.load会等到加载完DOM、脚本、CSS,最后加载完图片甚至是iframe中的所有资源才会触发,很多时候网页的图片较多较大,要等最后图片这个耗时大户加载完才执行js明显有些太迟了,很多时候都会影响用户体验。
很多js框架都有个document.ready的功能,像JQuery的$(document).ready()方法,可以在DOM加载完就立即执行js代码,让图片自个慢慢加载吧。
document.ready的核心是DOMContentLoaded事件,firefox、chrome、opera、safari、ie9+都可以使用addEventListener(‘DOMContentLoaded’,fn,false)进行事件绑定,而ie6~8不支持DOMContentLoaded事件,所以要针对ie6~8做兼容性处理。
资料上说ie6~8可以使用document.onreadystatechange事件监听document.readyState状态是否等于complete来判断DOM是否加载完毕,如果页面中嵌有iframe的话,ie6~8的document.readyState会等到iframe中的所有资源加载完才会变成complete,此时iframe变成了耗时大户。但是经过测试,即使页面中没有iframe,当readyState等于complete时,实际触发的是onload事件而不是DOMContentLoaded事件,对这点表示惊奇。
还好ie有个特有的doScroll方法,当页面DOM未加载完成时,调用doScroll方法时,就会报错,反过来,只要一直间隔调用doScroll直到不报错,那就表示页面DOM加载完毕了,不管图片和iframe中的内容是否加载完毕,此法都有效。
如果有多个js文件绑定了document.ready事件,为了防止浏览器重复绑定,同时有序执行,可以引入一个事件队列机制来解决。
以上就是document.ready事件的原理和兼容性问题,下面贴段实例代码,为了方便理解执行过程,使用函数封装的模式,执行过程都写在注释里了,如果有不妥之处欢迎指教。
//保存domReady的事件队列
eventQueue = []; //判断DOM是否加载完毕
isReady = false; //判断DOMReady是否绑定
isBind = false; /*执行domReady()
* www.111cn.net
*@param {function}
*@execute 将事件处理程序压入事件队列,并绑定DOMContentLoaded
* 如果DOM加载已经完成,则立即执行
*@caller
*/
function domReady = function(fn){
if (isReady) {
fn.call(window);
}
else{
eventQueue.push(fn);
}; bindReady();
}; /*domReady事件绑定
*
*@param null
*@execute 现代浏览器通过addEvListener绑定DOMContentLoaded,包括ie9+
ie6-8通过判断doScroll判断DOM是否加载完毕
*@caller domReady()
*/
function bindReady = function(){
if (isReady) return;
if (isBind) return;
isBind = true; if (window.addEventListener) {
document.addEventListener('DOMContentLoaded',execFn,false);
}
else if (window.attachEvent) {
doScroll();
};
}; www.111Cn.net /*doScroll判断ie6-8的DOM是否加载完成
*
*@param null
*@execute doScroll判断DOM是否加载完成
*@caller bindReady()
*/
function doScroll = function(){
try{
document.documentElement.doScroll('left');
}
catch(error){
return setTimeout(doScroll,20);
};
execFn();
}; /*执行事件队列
*
*@param null
*@execute 循环执行队列中的事件处理程序
*@caller bindReady()
*/
function execFn = function(){
if (!isReady) {
isReady = true;
for (var i = 0; i < eventQueue.length; i++) {
eventQueue[i].call(window);
};
eventQueue = [];
};
}; //js文件1
domReady(function(){
...
});
//js文件2
domReady(function(){
...
});
/注意,如果是异步加载的js就不要绑定domReady方法,不然函数不会执行,
//因为异步加载的js下载之前,DOMContentLoaded已经触发,addEventListener执行时已经监听不到了
测试页面:都加载了两张很大的图片,onload需要图片加载完才能执行js,DOMContentLoaded只需等到DOM加载完即可执行js。可以打开firebug查看加载过程,每次测试前记得先清理下浏览器缓存。
onload 例子
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>onload</title>
<style type="text/css">
.div{width: 200px;height: 200px;background: red;}
body{background: url('bg.jpg') no-repeat 0 0;}
</style> www.111cn.net
<!--script type="text/javascript" >
function loadscript(url){
var head=document.getElementsByTagName('head')[0];
var s=document.createElement('script');
s.type='text/javascript';
s.src=url;
s.async=false;
head.appendChild(s);
}
loadscript('go.js');
loadscript('onload2.js');
</script-->
<script type="text/javascript" src="go.js"></script>
<script type="text/javascript">
window.onload=function(){
$('.div').mousehover(function(){
$(this).css('background','green');
},function(){
$(this).css('background','red');
});
};
</script>
</head>
<body>
<div class="div">只有等到图片加载完,鼠标移到我身上才会变色呢。<br/><br/><br/><br/><br/>(因为所用框架不支持ie6,请用ie8+测试)</div>
<img src="bg.jpg">
<img src="bg2.jpg">
</body>
</html>
DOMContentLoaded例子
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>DOMContentLoaded</title>
<style type="text/css">
.div{width: 200px;height: 200px;background: red;}
body{background: url('bg.jpg') no-repeat 0 0;}
</style>
<!--script type="text/javascript" >
function loadscript(url){
var head=document.getElementsByTagName('head')[0];
var s=document.createElement('script');
s.type='text/javascript';
s.src=url;
s.async=false;
head.appendChild(s);
}
loadscript('go.js');
loadscript('onload2.js');
</script-->
<script type="text/javascript" src="go.js"></script>
<script type="text/javascript">
$(function(){
$('.div').mousehover(function(){
$(this).css('background','green');
},function(){
$(this).css('background','red');
});
})
</script>
</head>
<body>
<div class="div">在图片还没加载完之前,把鼠标移到我身上会变色哦.<br/><br/><br/><br/><br/>(因为所用框架不支持ie6,请用ie8+测试)</div>
<img src="bg.jpg">
<img src="bg2.jpg">
</body>
</html>
深入理解Javascript封装DOMContentLoaded事件的更多相关文章
- 深入理解javascript中的事件循环event-loop
前面的话 本文将详细介绍javascript中的事件循环event-loop 线程 javascript是单线程的语言,也就是说,同一个时间只能做一件事.而这个单线程的特性,与它的用途有关,作为浏览器 ...
- 理解Javascript中的事件绑定与事件委托
最近在深入实践js中,遇到了一些问题,比如我需要为动态创建的DOM元素绑定事件,那么普通的事件绑定就不行了,于是通过上网查资料了解到事件委托,因此想总结一下js中的事件绑定与事件委托. 事件绑定 ...
- 理解JavaScript中的事件轮询
原文:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 为什么JavaScript是单线程 JavaScript语言的一大特点就是单线程,也 ...
- 理解JavaScript中的事件路由冒泡过程及委托代理机制
当我用纯CSS实现这个以后.我开始用JavaScript和样式类来完善功能. 然后,我有一些想法,我想使用Delegated Events (事件委托)但是我不想有任何依赖,插入任何库,包括jQuer ...
- 理解javascript中的事件模型
javascript中有两种事件模型:DOM0,DOM2.而对于这两种的时间模型,我一直不是非常的清楚,现在通过网上查阅资料终于明白了一些. 一. DOM0级事件模型 DOM0级事件模型是早期的事件 ...
- 再次理解javascript中的事件
一.事件流的概念 + 事件流描述的是从页面中接收事件的顺序. 二.事件捕获和事件冒泡 + 事件冒泡接收事件的顺序:
- 理解javascript封装
封装可以被定义为对对象的内部数据表现形式和实现细节进行隐藏.通过封装可以强制实施信息隐藏. 在JavaScript中,并没有显示的声明私有成员的关键字等.所以要想实现封装/信息隐藏就需要从另外的思路出 ...
- 理解JavaScript中的事件流
原文地址:http://my.oschina.net/sevenhdu/blog/332014 目录[-] 事件冒泡 事件捕获 DOM事件流 当浏览器发展到第四代时(IE4和Netscape Comm ...
- 彻底理解javascript 中的事件对象的pageY, clientY, screenY的区别和联系。
说到底, pageY, clientY, screenY的计算,就是要找到参考点, 它们的值就是: 鼠标点击的点----------- 和参考点指点----------的直角坐标系的距离 stacko ...
随机推荐
- Window 7 Professional 多语言设置
1. 正常情况下,WINDOW系统只提供企业和旗舰版的语言切换的界面设置,其他版本没有. 2. 首先下载语言包,然后解压待用. 3. 以管理员身份运行命令窗口,如下输入: 4. 上面完成后,下载 ht ...
- 《C#多线程编程实战》1.11 Monitor.TryEnter()避免死锁
这章的内容是真的有意思 特别是代码. 先贴上代码: class Program { static void Main(string[] args) { object lock1 = new objec ...
- 【bzoj4806~bzoj4809】 象棋四连发 DP-高精度-匈牙利算法-dfs
都是经典题了吧..我好无聊.. 4806 4806-1801是双倍经验..DP方程看代码吧.. /* http://www.cnblogs.com/karl07/ */ #include <cs ...
- saltstack平台基础
saltstack概述saltstack是基于python开发的一套C/S架构配置管理工具,使用SSL证书签方的方式进行认证管理底层使用ZeroMQ消息队列pub/sub方式通信 号称世界上最快的 ...
- Docker安装FastDFS
什么是FastDFS? FastDFS 是用 c 语言编写的一款开源的分布式文件系统.FastDFS 为互联网量身定制, 充分考虑了冗余备份.负载均衡.线性扩容等机制,并注重高可用.高性能等指标,使用 ...
- Java面向对象之内部类(匿名内部类)
一.基础概念 匿名内部类:简化书写的内部类.其实匿名内部类就是一个子类对象. 前提:内部类需要继承或者实现外部的类或者接口. 格式:new 父类或者接口(){定义子类的内容} 二.将内部类定义到局部的 ...
- controller运行springboot项目
搭建完springboot项目后,新建HelloController.java文件,编写main方法,启动HelloController.java,具体代码如图: 在浏览器访问127.0.0.1:80 ...
- AF 与 PF区别
AF 表示ADDRESS FAMILY 地址族 PF 表示PROTOCL FAMILY 协议族 Winsock2.h中#define AF_INET 0#define PF_INET AF_INET ...
- js 获取 屏幕 可用高度...
document.documentElement.clientWidth 此方法适用于手机... document.documentElement.clientHeight (浏览器(手机或电脑)可用 ...
- 1001 害死人不偿命的(3n+1)猜想 (15 分)
#include <iostream> using namespace std; int main(){ ; cin >> n; ){ != ) n = ( * n + ) / ...