我的博客已经写过好几篇如何实现domReady的文章,最近做培训,面向新手们,需要彻彻底底向他们说明这个东西,于是就有了这篇文章。

我们经常看人们用


document.getElementById("xxx").style.left = "80px"

报错,说找不到元素.但明明页面上有包含xxx这个ID的元素的

这其实就是分不清HTML标签与DOM节点之故了。

HTML是一种标记语言, 告诉我们这页面有什么内容。
但行为交互是需要通过DOM操作实现, 不要以后那两个尖括号的内容就是一个DOM

HTML标签要通过浏览器解析才会变成DOM节点。

当我们向地址栏传入一个URL, 开始加载页面到我们看到内容,这期间就有一个DOM节点构建的过程。

节点们是以树的形式组织的, 当页面上所有HTML都转换为节点, 这就叫做DOM树建完, 简称之domReady。

HTML转换DOM是一个非常复杂的过程,想深入的同学可以看一下这个地址

我们简单说一下, 浏览器是从上到下, 从左到右,一个个字符串读入, 大致可以认为两个同名的开标签与闭标签就是一个DOM(有的是没有闭签),这时就忽略掉它的两个标签间的内容。页面上有许多标签, 但标签会生成同样多的DOM,因为有的标签下只允许存在特定的子标签,
比如tr下面一定是td,th, select下面一定是opgroup,option,而option下面,就算你写了<span></span>,它都会忽略掉,option下面只存在文本,这就是我们需要自定义下拉框的缘故.

我们说过, 这顺序是从上到下, 有的元素很简单,会构建得很快,但标签存在src, href属性,它会引用外部资源,这就要区别对待了.比如说, script标签,它一定会等src指定的脚本文件加载下来,然后全部执行了里面的脚本,才会分析下一个标签.这种现象叫做堵塞.

堵塞是一种非常致命的现象,因为浏览器渲染引擎是单线程的,如果头部脚本过多过大会导致白屏,影响用户体验,因此雅虎的20军规就有一条提到 ,将所有script标签放到body之后.

此外, style标签与link标签,它们在加载样式文件时是不会堵塞,但它们一旦异步加载好,就立即开始渲染已经构建好的元素节点们, 这可能会引起reflow, 这也影响速度.

另一个影响DOM树构建的因此是iframe,它也会加载资源, 虽然不会堵塞DOM构建,但它由于是发出HTTP请求,而HTTP请求是有限,它会与父标签的其他需要加载外部资源的标签产生竞争。我们经常看到一些新闻网,上面会挂许多iframe广告, 这些页面一开始加载时就很卡,也是这缘故.

此外还有object元素, 用来加载flash

等等,这些东西都会影响到DOM树的构建过程.因此在这时候,当我们贸贸然,使用getElementById, getElementsByTagName获取元素,然后操作它们, 就会有很大机率碰到 元素为null的 异常. 这时, 目标元素还可以没有转换为DOM节点, 还只是一个普通的字符串呢!

我们又不能随意写一个

setTimeout(function(){
document.getElementById("xxx").style.left = "80px"
}, 3000)

这完全是靠蒙, 可能有效, 也可能失败. 因此获得 所有标签都转换为DOM节点的时机就非常重要.

很早期, 浏览器提供了一个window.onload方法,但这东西是等到所有标签变成DOM,并且外部资源,图片,背景音乐什么都加载好才触发, 时间上有点晚.

幸好,浏览器提供了一个document.readyState属性,当它变成complete时,说明这时机到了

但这是一个属性,不是一个事件,需要使用不太精确的setInterval轮询

在标签浏览器, W3C终于绅士地提供了一个DOMContentLoaded事件;在旧式IE下,也可以勉强使用onreadystatechange事件模拟, 直接某一天,有个外国大牛发掘出doScroll这个伟大的hack, 它让我们在IE下更接近DOMContentLoaded的效果

function IEContentLoaded (w, fn) {
var d = w.document, done = false,
// 只执行一次用户的回调函数init()
init = function () {
if (!done) {
done = true;
fn();
}
};
(function () {
try {
// DOM树未创建完之前调用doScroll会抛出错误
d.documentElement.doScroll('left');
} catch (e) {
//延迟再试一次~
setTimeout(arguments.callee, 50);
return;
}
// 没有错误就表示DOM树创建完毕,然后立马执行用户回调
init();
})();
//监听document的加载状态
d.onreadystatechange = function() {
// 如果用户是在domReady之后绑定的函数,就立马执行
if (d.readyState == 'complete') {
d.onreadystatechange = null;
init();
}
};
}

这里有一些主流框架对domReady的实现

其实都大同小异, 关键是 设置一个数组, 当domReady这个时刻没有到时,先将回调放到数组里; 然后是各种检测domReady的方法(如DOMContentLoaded, onreadystatechange, doScroll hack, document.readyState轮询 ), 一旦到了,就执行这个数组所有回调,
并且以后用户再进入这个方法, 就不放数组,直接执行.

上完这课,以后大家操作节点的逻辑,一定要写在domReady回调中啊

何谓domReady的更多相关文章

  1. 谈谈DOMContentLoaded:Javascript中的domReady引入机制

    一.扯淡部分 回想当年,在摆脱写页面时js全靠从各种DEMO中copy出来然后东拼西凑的幽暗岁月之后,毅然决然地打算放弃这种处处“拿来主义”的不正之风,然后开启通往高大上的“前端攻城狮”的飞升之旅.想 ...

  2. domReady的实现

    我们都知道JQ的 $(document).ready(fn) 方法.可以在页面准备就绪后才执行脚本,该方法相比传统的window.onload 事件,它的优势体现于onload事件是需要等到页面中所有 ...

  3. web前端学习部落22群 明白何谓Margin Collapse

    明白何谓Margin Collapse 不同于其他很多属性,盒模型中垂直方向上的Margin会在相遇时发生崩塌,也就是说当某个元素的底部Margin与另一个元素的顶部Margin相邻时,只有二者中的较 ...

  4. understand dojo/domReady!

    require(["dojo/dom", "dojo/domReady!"], function(dom){ dom.byId("helloworld ...

  5. [转] 主流JS框架中DOMReady事件的实现

    在实际应用中,我们经常会遇到这样的场景,当页面加载完成后去做一些事情:绑定事件.DOM操作某些结点等.原来比较常用的是window的onload 事件,而该事件的实际效果是:当页面解析/DOM树建立完 ...

  6. Unity3D热更新全书-何谓热更新,为何热更新,如何热更新

    首先来赞叹一下中文,何谓为何如何,写完才发现这三个词是如此的有规律. 为何赞叹中文?因为这是一篇针对新手程序员的文字,是一节语文课. 然后来做一下说文解字,也就是 何谓热更新 热更新,每个程序员一听就 ...

  7. domReady方法(dom加载完成执行回调)

    var domReady = function( fn ) { var isReady = false, ready = function(){ if(!isReady){ typeof fn === ...

  8. IE9或以上的浏览器flash值为空时,导致domready不触发

    在前些时间开发中遇到一个问题当flash值<param name="movie" value=""/>为空时,IE版本>=9不会触发domre ...

  9. 第二课:判断js变量的类型以及domReady的原理

    1.类型的判断: js五种简单数据类型有:null,undefined,boolean,number,string. 还有复杂的数据类型:Object,Function,RegExp,Date,自定义 ...

随机推荐

  1. RedHat使用Centos的yum仓库

    RedHat使用Centos的yum仓库 卸载红帽yum源 [root@zhouwanchun ~]# rpm -qa | grep yum [root@zhouwanchun ~]# rpm -e ...

  2. echarts3更新

    dataRange: { realtime: false, itemHeight: 80, splitNumber:6, borderWidth:1, textStyle: { color: '#33 ...

  3. 关于javascript三目

    三目运算符能使我们的代码更为简洁,因而包括小编的我也很是青睐它,不过有时候我们给予它更多的希望,小编处于学习阶段,先从笔记开始: (3>1)?console.log(1):console.log ...

  4. (转)CLR20R3 程序终止的几种解决方案

    原文地址:https://blog.csdn.net/fxfeixue/article/details/4466899 这是因为.NET Framework 1.0 和 1.1 这两个版本对许多未处理 ...

  5. 17 RAID与mdadm管理命令

    在"14 磁盘及文件系统管理详解"中,我们详细介绍了磁盘的工作原理,但是,有一点我们一定要明白,作为现在存储数据的主要设备,机械磁盘早就是上个世纪的产品,而它的读写速度与内存.CP ...

  6. Can't use Subversion command line client***Probably the path to Subversion executable is wrong. Fix

    新同事要入职,赶紧收拾一下电脑:再使用SVNupdate代码时,显示Nothing to show.androidstudio不能使用svn了: 是这样婶滴,报错信息 什么情况: 好了:问题叙述完毕开 ...

  7. replace实现替换全部

    方法: string.replace(new RegExp(oldString,"gm"),newString)) gm g=global, m=multiLine , 大致上方法 ...

  8. thinkphp3.2.2有预览的多图上传

    thinkphp3.2.2有预览的多图上传 整体思路 1 封装文件上传和图片上传的类文件 2 视图中添加相关JS和表单提交 3 控制器中添加上传文件的相关代码 一 2个class 文件 请上传到/Th ...

  9. 最近玩了下linux下的lampp注意一些使用

    最近玩了下linux下的lampp注意一些使用 1 配置文件 /opt/lampp/etc 2 一些命令 mysql命令 /opt/lampp/bin/mysql php命令 /opt/lampp/b ...

  10. 34.scrapy解决爬虫翻页问题

    这里主要解决的问题: 1.翻页需要找到页面中加载的两个参数. '__VIEWSTATE': '{}'.format(response.meta['data']['__VIEWSTATE']), '__ ...