DOM树【Document Object Model】
介绍事件模型之前,先简单看下DOM
树的结构:
DOM
树就像是一颗倒长着的大树,这样的对象模型决定了节点之间都有一定的关联。它们关系可能有父子、有兄弟,我们可以顺着这颗树做出许多操作)。- 每个
HTML
页面都是一颗DOM
树,浏览器在解析HTML
代码时,会按照代码结构将代码解析成一颗树状结构,每个HTML
标签对应DOM
树上相应的结点。 - 下面是一棵最简单的
DOM
树:
<!DocType html>
<head>
<title>eventTest</title>
<meta name="author" content="Ginna">
<style>
div{
width: 200px;
height: 100px;
background-color: red
}
ul{
width:80px;
height: 80px;
background-color: blue
}
li{
width:40px;
height: 40px;
background-color: yellow
}
</style>
</head>
<body>
<div id="c">
<ul>
<li>
<a>点 我</a>
</li>
</ul>
</div>
<script>
var c=document.getElementById('c');
c.addEventListener('click',function(e){
console.log(e.target.nodeName)
});
</script>
</body>
</html>
事件模型【捕获与冒泡】
代码中对div
点击事件click
的监听结果:
- 当点到a标签区域 “点我” 时,控制台打印出的是
A
- 当点击黄色区域时,打印出
LI
- 当点击外面蓝色区域时,打印出
UL
- 当点击红色区域时,打印出
DIV
上述案例中,我们给div添加一个事件响应函数
var c=document.getElementById('c');
c.addEventListener('click',function(e){
console.log(e.target.nodeName)
});
事件流
事件流描述的就是从页面中接收事件的顺序。而IE和Netscape提出了完全相反的事件流概念。IE事件流是事件冒泡,而Netscape的事件流就是事件捕获。
打个比喻,如果你把手指放在圆心上(多个同心圆),那么你的手指指向的不是一个圆,而是纸上所有的圆。如果你单击了某个按钮,在单击按钮的同时,也单击了按钮的容器元素,甚至是整个页面。
事件捕获
事件捕获的思想是不太具体的节点应该更早的接收到事件,而在最具体的节点应该最后接收到事件。事件捕获的用以在于事件到达预定目标之前捕获它。IE9+、Safari、Chrome、Opera和Firefox支持,且从window开始捕获(尽管DOM2级事件规范要求从document)。由于老版本浏览器不支持,所以很少有人使用事件捕获。
当点击“点我”的时候,浏览器就会寻找发生点击事件的事件源,从上到下,依次找到
body->div->ul->li->a
。这个从上到下寻找事件源的过程就叫做事件捕获(阶段)。
事件冒泡
IE的事件流叫做事件冒泡。即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。所有现代浏览器都支持事件冒泡,并且会将事件一直冒泡到window对象。
找到了事件源
a
标签,a
标签上的点击事件就会像泡泡一样传递到它的上一级li
,进而传递到ul
、div
、body
,这个与事件捕获传递方向相反的过程就叫做事件冒泡(阶段)。
取消事件冒泡
使用event
对象的stopPropagation()
方法。如果在a、li、ul
这三个节点任一节点上使用stopPropagation()
方法,冒泡过程就会终止,div
就监听不到点击事件,它的响应函数也不会执行
var li=c.getElementsByTagName('li')[0];
li.addEventListener('click',function(e){
console.log(this.tagName);
e.stopPropagation();
})
在li
上取消事件冒泡,这时再点击a标签,事件传递到li
上就会停止,console.log
只会输出li
而不会输出a。如果点击的是ul
标签,console.log
会输出ul
,这是另外一个点击事件,中间没有取消冒泡,妥妥的被div
捕捉到。
DOM事件流
- DOM0级事件(默认发生在冒泡阶段.只能绑定一个事件)
//事件绑定
ele.onclick = function (){
//
}
//解除绑定
ele.onclick = null;
- DOM2级事件(默认发生在冒泡阶段,由第三个参数决定,可绑定多个事件)
DOM2
级事件规定事件流包括三个阶段,事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的事件捕获,为截获事件提供了机会。然后是实际的目标接收了事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。
//事件绑定
ele.addEventListener(eventType, handler, useCapture)
/**
2015年底,为了扩展新的选项,从而自定义更多的行为
DOM 规范做了修订:addEventListener() 的第三个参数可为 {} 对象
*/
el.addEventListener(type, listener, {
capture: false, // === useCapture
once: false, // 是否设置单次监听
passive: false // 是否让 阻止默认行为(preventDefault()) 失效
})
//eventType不带on,如click
//IE下用attachEvent
ele.attachEvent(eventType, handler);
//解除绑定
ele.removeEventListener(eventType,handler);
事件委托
事件委托就是利用冒泡的原理,把事件加到父元素或祖先元素上,触发执行效果。
事件绑定
- 在DOM元素中直接绑定;
<!--
我们可以在DOM元素上绑定
onclick、ondblclick、onkeydown、onkeypress、onkeyup、
onmouseover、onmouseout、onmousedown、onmouseup、等。。
如果想知道更多事件类型请查看, DOM事件 。
-->
<input type="button" value="click me"
onclick="hello()">
<script>
function hello(){
alert("hello world!");
}
</script>
- 在JavaScript代码中绑定;
<!--JavaScript代码与HTML标签分离,
文档结构清晰,便于管理和开发。-->
<input type="button" value="click me" id="btn">
<script>
document.getElementById("btn").onclick = function(){
alert("hello world!");
}
</script>
- 绑定事件监听函数。
<!--
涉及到上述提到的3个阶段
IE8以下不支持
event : (必需)事件名,支持所有 DOM事件 。
function:(必需)指定要事件触发时执行的函数。
useCapture:(可选)指定事件是否在捕获或冒泡阶段执行。
true,捕获。false,冒泡。默认false。
element.addEventListener(event, function, useCapture)
-->
<input type="button" value="click me" id="btn1">
<script>
document.getElementById("btn1").addEventListener("click",hello);
function hello(){
alert("hello world!");
}
</script>
<!--
ie标准
event:(必需)事件类型。需加on,例如:onclick。
function:(必需)指定要事件触发时执行的函数。
element.attachEvent(event, function)
-->
<input type="button" value="click me" id="btn2">
<script>
document.getElementById("btn2").attachEvent("onclick",hello);
function hello(){
alert("hello world!");
}
</script>
事件监听
- 事件监听的优点
// a.绑定多个事件 (on不行,执行最后一个)
var btn = document.getElementById("btn");
btn.addEventListener("click",fun1);
btn.addEventListener("click",fun2);
// b.可以解除相应的绑定
btn.removeEventListener("click",fun2);
- 事件监听的封装
<input type="button" value="click" id="btn">
//绑定监听
function addEventHandler(target,type,fn){
if(target.addEventListener){
target.addEventListener(type,fn);
}else{
target.attachEvent("on"+type,fn);
}
}
//移除监听
function removeEventHandler(target,type,fn){
if(target.removeEventListener){
target.removeEventListener(type,fn);
}else{
target.detachEvent("on"+type,fn);
}
}
//test,除了click还可以执行其他事件
var btn = document.getElementById("btn");
addEventHandler(btn,"click",fun);
removeEventHandler(btn,"click",fun);
事件委托
事件委托就是利用冒泡的原理,把事件加到父元素或祖先元素上,触发执行效果
<div id='c'>
<ul>
<li>
<a id='btn'>点 我</a>
</li>
</ul>
</div>
<script>
var c=document.getElementById('c');
var btn=document.getElementById('btn');
c.addEventListener('click',function(e){
if(e.target == btn){
alert("我是链接呀!!!");
}
});
</script>
事件委托优点
- 提高JavaScript性能。事件委托可以显著的提高事件的处理速度,减少内存的占用。
<!--传统写法-->
<ul id="lst">
<li id="it1" >item1</li>
<li id="it2" >item2</li>
</ul>
<script>
var item1 = document.getElementById("it1");
var item2 = document.getElementById("it2");
item1.onclick = function(){
alert("hello item1");
}
item2.onclick = function(){
alert("hello item2");
}
</script>
<!--事件委托-->
<ul id="lst">
<li id="it1" >item1</li>
<li id="it2" >item2</li>
</ul>
<script>
var item1 = document.getElementById("it1");
var item2 = document.getElementById("it2");
document.addEventListener("click",function(event){
var target = event.target;
if(target == item1){
alert("hello item1");
}else if(target == item2){
alert("hello item2");
}
})
</script>
- 动态的添加DOM元素,不需要因为元素的改动而修改事件绑定。
<!--传统写法-->
<ul id="lst">
<li id="it1" >item1</li>
<li id="it2" >item2</li>
</ul>
<script>
var list = document.getElementById("lst");
var item = list.getElementsByTagName("li");
for(var i=0;i<item.length;i++){
(function(i){
item[i].onclick = function(){
alert(item[i].innerHTML);
}
})(i)
}
var node=document.createElement("li");
var textnode=document.createTextNode("itemN");
node.appendChild(textnode);
list.appendChild(node);
</script>
<!--点击item1/item2都有事件响应,点击itemN时,没有事件响应。
传统的事件绑定无法对[动态添加的元素][动态添加事件]。-->
<!--事件委托-->
<ul id="lst">
<li id="it1">item1</li>
<li id="it2">item2</li>
</ul>
<script>
var list = document.getElementById("lst");
document.addEventListener("click",function(event){
var target = event.target;
if(target.nodeName == "LI"){
alert(target.innerHTML);
}
})
var node=document.createElement("li");
var textnode=document.createTextNode("itemN");
node.appendChild(textnode);
list.appendChild(node);
</script>
<!--itemN有事件响应。
说明事件委托可以为新添加的DOM元素动态的添加事件。-->
浏览器渲染页面的过程
参考链接
其他…