最近跟同事一起面试,发现同时每次必问JS事件相关的内容,自己就做了下梳理,对其事件相关的内容做了下总结。
事件模型:
顾名思义就是事件的一个流转规则,说到事件模型就跟各个浏览器之间的差异就有关系了,目前浏览器没有统一事件模型,大致分为三种原始事件模型(DOM0级)、DOM2事件模型、IE事件模型。
- 原始事件模型:被所有浏览器支持,原始事件没有事件流,立马触发立马响应不会传递,我自己理解就是点击只触发当前,类似阻止传递。
//主要就是onclick事件
(1)在html代码中直接指定属性值:<button id="demo" type="button" onclick="doSomeTing()" />
(2)在js代码中为 document.getElementsById("demo").onclick = doSomeTing()
优点:所有浏览器都兼容
缺点:
1.逻辑与显示没有分离;
2.相同事件的监听函数只能绑定一个,后绑定的会覆盖掉前面的,如:a.onclick = func1; a.onclick = func2;将只会执行func2中的内容。
3.无法通过事件的冒泡、委托等机制(后面会讲到)完成更多事情。
因为这些缺点,虽然原始事件类型兼容所有浏览器,但仍不推荐使用,当然一般情况我们也很少使用。
-
IE事件模型:IE事件模型共有两个过程:
事件处理阶段:事件到达目标元素, 触发目标元素的监听函数。
事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行。
先执行元素的监听函数,然后事件沿着父节点一直冒泡到document。冒泡机制后面系列会讲,此处暂记。IE模型下的事件监听方式也挺独特,绑定监听函数的方法是:attachEvent( "eventType","handler"),其中evetType为事件的类型,如onclick,注意要加’on’。解除事件监听器的方法是 detachEvent("eventType","handler" ) -
DOM2事件模型:这种形式是W3C制定的标准模型,现代浏览器(IE6~8除外)都已经遵循这个规范。W3C制定的事件模型中,一次事件的发生包含三个过程:1.事件捕获阶段,2.事件目标阶段,3.事件冒泡阶段。下边两个图对该事件模型讲解的很清楚。
图1
图2
其实我们之后要讲的事件冒泡、事件捕获、事件委托都是以此模型来讲解的。
事件冒泡
事件从最具体的元素到不具体的元素,从当前触发的事件目标一级一级往上传递,依次触发,直到document为止。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>xxx</title>
</head>
<body onclick="bodyClick()">
<div onclick="divClick()">
<button onclick="btn()">
<p onclick="p()">点击冒泡</p>
</button>
</div>
<script>
function p(){
console.log('p被点击')
}
function btn(){
console.log("button被点击")
}
function divClick(event){
console.log('div被点击');
}
function bodyClick(){
console.log('body被点击')
}
//也可以使用
addEventListener('click',function(){ },false);来替换onclick。
</script>
</body>
</html>
p被点击》button被点击》div被点击》body被点击,即具体元素到不具体元素,从p冒泡到根节点。
- 阻止事件冒泡:
标准的W3C 方式:e.stopPropagation();这里的stopPropagation是标准的事件对象的一个方法,调用即可
非标准的IE方式:ev.cancelBubble=true; 这里的cancelBubble是 IE事件对象的属性,设为true就可以了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<button>
<p>点击捕获</p>
</button>
</div>
<script>
var _p=document.querySelector('p');
var _b=document.querySelector('button');
var _d=document.querySelector('div');
var _body=document.querySelector('body');
_p.addEventListener('click',function(){
console.log('p被点击')
},true);
_b.addEventListener('click',function(){
console.log("button被点击")
},true);
_d.addEventListener('click', function(){
console.log('div被点击')
},true);
_body.addEventListener('click',function(){
console.log('body被点击')
},true);
</script>
</body>
</html>
事件捕获
从不具体的元素到具体的元素,从document开始触发,一级一级往下传递,依次触发,直到真正事件目标为止。
body被点击》div被点击》buton被点击》p被点击,即跟冒泡是相反的,从不具体元素到具体元素,从根节点到p标签。
事件委托
- 原理:事件冒泡机制
利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。借网上别人的例子简单明了的解释:
有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。 - 优点:
1.大量减少内存占用,减少事件注册。
2.新增元素实现动态绑定事件
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
- 实现方式:
1.可用addEventListener(); //所有主流浏览器,除了IE8及更早IE版本。
ul.addEventListener("click",function(e) {
if(e.target && e.target.nodeName.toLowerCase() == "li") { // 检查事件源e.target是否为li
console.log("List item ",e.target.id.replace("post-","")," was clicked!"); // 打印当前点击是第几个item
}
});
2.attachEvent() //IE8及IE更早版本,现在已经不怎么使用。
<1>.用法:element.attachEvent(event,function);
(1)event事件加'on',onClick
(2)没有第三个参数,因为IE只有冒泡,没有反向冒泡。
(3)执行顺序按照绑定的反序(先执行后绑定的方法)。
<2>.移除事件监听:element.detachEvent(event,function)
以上就是JS事件冒泡、捕获、委托和事件模型的相关个人理解,有错误之处还请指正。
网友评论