被遗忘的 DOM

事件流

描述的是从页面中接受事件的顺序。

  • 事件捕获流
  • 事件冒泡流

事件冒泡:即事件最开始由最具体的元素(文档中嵌套层次最深的那个节点)接受,然后逐级向上传播至最不具体的那个节点(文档)。
事件捕获:不太具体的节点应该更早接受到事件,而最具体的节点最后接受到事件。

事件处理程序

  1. HTML 事件处理程序: button onclick=”showTextModel()”
  2. DOM0 级事件处理程序: var btn = document.getElementById(“btn”); btn.onclick = function(){} btn.onclick = null;//删除事件
  3. DOM2 级事件处理程序:addEventListener() 和 removeEventListener。参数:处理事件名、处理事件程序、布尔值。true 事件捕获时触发,false 事件冒泡时触发。
  4. IE 事件处理程序:attachEvent() 添加事件 detachEvent 删除事件 参数:处理史建明、处理事件程序。IE8 以及更早的浏览器版本只支持事件冒泡。
  5. 跨浏览器的事件处理程序
1
2
3
4
5
6
7
8
9
10
11
var eventUtil = {
addHandler: function(element, type , handler){
if(element.addEventlistener){
element.addEventlistene(type,handler,false);
}else if(element.attachEvent){
element.attachEvent('on'+type,handler);
}else{
element['on'+type] = handler
}
}
}

事件对象

在触发 DOM 上的事件时都会产生一个对象

事件对象 event
DOM 中的事件对象
type 属性:用于获取事件类型
target 属性:用于获取事件目标
offsetWidth 属性:水平方向 width + 左右padding + 左右border-width
clientWidth 属性:水平方向 width + 左右padding
stopPropagation()方法 用于阻止事件冒泡
preventDefault()方法 阻止事件的默认行为

IE 中的事件对象
type 属性:用于获取事件类型
srcElement属性:用于获取事件目标
window.event
cancelBubble 属性 用于阻止事件冒泡 true 阻止事件冒泡 false 不阻止事件冒泡
returnValue 属性 用于阻止事件的默认行为 true 阻止事件默认行为 false 不阻止事件默认行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
var eventUtil={
// 添加句柄
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent('on'+type,handler);
}else{
element['on'+type]=handler;
}
},
// 删除句柄
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent('on'+type,handler);
}else{
element['on'+type]=null;
}
},

getEvent:function(event){
return event?event:window.event;
},

getType:function(event){
return event.type;
},

getElement:function(event){
return event.target || event.srcElement;
},

preventDefault:function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue=false;
}
},

stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble=true;
}
}
}

事件类型

鼠标事件

  • click 点击
  • dblclick 双击
  • mouseover、mouseout 鼠标移入、移除
  • mouseenter、mouseleave 鼠标移入、移除

键盘事件

  • keyDown 按下键盘上任意键时触发
  • keyUp 释放键盘上的键时触发
  • keyPress 按下键盘上字符键时触发

节点

节点的类型

节点的类型

文档片段节点

1
2
3
4
5
var frag = document.createDocumentFragment();
var li = document.createElement("li");
li.innerHTML = "List item ";
frag.appendChild(li);
document.getElementById("list-node").appendChild(frag)

判断节点类型

1
2
3
4
5
6
7
8
var containerElement = document.getElementById("container");
containerElement.nodeType == Node.ELEMENT_NODE
containerElement.nodeType == 1
containerElement.nodeName == 'div'
containerElement.nodeValue == 'Falldown'
containerElement.attributes[0]
containerElement.childNodes[0]
document.doctype

元素节点的判断

isElement() 判定是否为元素节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var testDiv = document.createElement("div");
var isElement = function (obj) {
if (obj && obj.nodeType === 1) {
//先过滤最简单的
if (window.Node && obj instanceof Node) {
//如果是IE9,则判定其是否Node的实例
return true; //由于obj可能是来自另一个文档对象,因此不能轻易返回false
}
try {
//最后以这种效率非常差但肯定可行的方案进行判定
testDiv.appendChild(obj);
testDiv.removeChild(obj);
} catch (e) {
return false;
}
return true;
}
return false;
};
isElement(document.getElementByID("test"));

isHTML 判断是不是 HTML 节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var testDiv = document.createElement('div');
var isElement = function (obj) {
if (obj && obj.nodeType === 1) {//先过滤最简单的
if( window.Node && (obj instanceof Node )){ //如果是IE9,则判定其是否Node的实例
return true; //由于obj可能是来自另一个文档对象,因此不能轻易返回false
}
try {//最后以这种效率非常差但肯定可行的方案进行判定
testDiv.appendChild(obj);
testDiv.removeChild(obj);
} catch (e) {
return false;
}
return true;
}
return false;
}

var isHTMLElement(el){
if(isElement){
return isXML(el.ownerDocument);
}
return false;
}

isXML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//Sizzle, jQuery自带的选择器引擎
var isXML = function (elem) {
var documentElement =
elem && (elem.ownerDocument || elem).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
};
console.log(isXML(document.getElementById("test")));

//但这样不严谨,因为XML的根节点,也可能是HTML标签,比如这样创建一个XML文档
try {
var doc = document.implementation.createDocument(null, "HTML", null);
console.log(doc.documentElement);
console.log(isXML(doc));
} catch (e) {
console.log("不支持creatDocument方法");
}

//我们看看mootools的slick选择器引擎的源码:
var isXML = function (document) {
return (
!!document.xmlVersion || !!document.xml || toString.call(document) == "[object XMLDocument]" || (document.nodeType == 9 && document.documentElement.nodeName != "HTML"));
};

//精简版
var isXML = window.HTMLDocument
? function (doc) {
return !(doc instanceof HTMLDocument);
} : function (doc) {
return "selectNodes" in doc;
};

var isXML = function (doc) {
// 判断返回大小写区分 xml 和 html
return (doc.createElement("p").nodeName !== doc.createElement("P").nodeName);
};

contains 判断是否是其子节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 var pNode = document.getElementById("p-node");
var cNode = document.getElementById("c-node").childNodes[0];
alert(pNode.contains(cNode));

function fixContains(a, b) {
try {
while ((b = b.parentNode)){
if (b === a){
return true;
}
}
return false;
} catch (e) {
return false;
}
}
var pNode = document.getElementById("p-node");
var cNode = document.getElementById("c-node").childNodes[0];
alert(fixContains(pNode, cNode));

节点的 nodeName 与 nodeValue

浏览器渲染引擎的基本渲染流程

解析 HTML 构建 DOM 树(构建 DOM 节点) -> 构建渲染树(解析样式信息) -> 布局渲染树(布局 DOM 节点) -> 绘制渲染树(绘制 DOM 节点)

Webkit 主要渲染流程

domReady

1
window.onload

domReady 实现策略

在页面的 DOM 树创建完成后(也就是 HTML 解析第一步完成)即触发,而无需等待其他资源的加载。即 DOMReady 实现策略:
1、支持 DOMContentLoaded 事件的,就使用 DOMContentLoaded 事件
2、不支持的,就用来自 Diego Perini 发现的著名 Hack 兼容。兼容原理大概就是,通过 IE 中的 document.docuemntElement.doScroll(‘left’) 来判断 DOM 树是否创建完毕。

实现 domReady

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function myReady(fn){
//对于现代浏览器,对DOMContentLoaded事件的处理采用标准的事件绑定方式
if ( document.addEventListener ) {
document.addEventListener("DOMContentLoaded", fn, false);
} else {
IEContentLoaded(fn);
}

//IE模拟DOMContentLoaded
function IEContentLoaded (fn) {
var d = window.document;
var done = false;

//只执行一次用户的回调函数init()
var 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();
}
}
}
}

DOM 节点继承层次



HTML 嵌套规则

HTML存在许多种类型的标签,有的标签下面只允许特定的标签存在,这就叫做 HTML 嵌套规则。

不按 HTML 嵌套规则写,浏览器就不会正确解析,会将不符合嵌套规则的节点放到目标节点的下面,或者变成纯文本。

块状元素:一般是其他元素的容器,可容纳内联元素和其他块状元素,块状元素排斥其他元素与其位于同一行,宽读高度起作用。常见块状元素为 div 和 p。

内联元素:内联元素只能容纳文本或者其他内联元素,它允许其他内联元素与其位于同一行,但宽度高度不起作用。常见内联元素为 a。

块状元素与内联元素嵌套规则

1、块元素可以包含内联元素或某些块元素,但内联元素却不能包含块元素,它只能包含其他内联元素。
2、块元素不能放在 p 元素内。
3、有几个特殊的块级元素只能包含内联元素,不能包含块级元素。:h1 ~ h6
4、块级元素与块级元素并列、内联元素与内联元素并且




相关资料
[视频]DOM事件探秘
[视频]DOM探索之基础详解篇

盘点HTML字符串转DOM的各种方法及细节

javascript中的offsetWidth、clientWidth、innerWidth及相关属性方法
前端必读:浏览器内部工作原理
[转] 主流JS框架中DOMReady事件的实现
如何检查JavaScript对象是否为DOM对象?


相关代码
DOM

You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.