1 事件对象
事件在浏览器中是以对象的形式存在的,即event。触发一个事件,就会产生一个事件对象event,该对象包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。
所有浏览器都支持event对象,但支持方式不同,在DOM中event对象必须作为唯一的参数传给事件处理函数,在IE中event是window对象的一个属性。
DOM中事件对象重要属性和方法。
属性:
- type属性,用于获取事件类型
- target属性 用户获取事件目标 事件加在哪个元素上。(更具体target.nodeName)
方法:
- stopPropagation()方法 用于阻止事件冒泡
- preventDefault()方法 阻止事件的默认行为 移动端用的多
IE中的事件对象
- 通过DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。
- 第二种情况:通过attachEvent()添加的事件处理程序,event对象作为参数传入。
属性:
- type属性,用于获取事件类型(一样)
- srcElement属性,用户获取事件目标 事件加在哪个元素上。
- cancelBubble属性 用于阻止事件冒泡 IE中cancelBubble为属性而不是方法,true表示阻止冒泡。
- returnValue属性 阻止事件的默认行为 false表示阻止事件的默认行为。
Event对象的一些兼容性写法:
- 获得event对象兼容性写法
event || (event = window.event);- 获得target兼容型写法
event.target||event.srcElement- 阻止浏览器默认行为兼容性写法
event.preventDefault ? event.preventDefault() : (event.returnValue = false);- 阻止冒泡写法
event.stopPropagation ? event.stopPropagation() : (event.cancelBubble = true);
2 事件流
事件流描述的是从页面中接收事件的顺序。事件发生时会在元素节点与根节点之间按照特定的顺序传播,路径所经过的所有节点都会收到该事件,这个传播过程即DOM事件流。
事件传播的顺序对应浏览器的两种事件流模型:捕获型事件流和冒泡型事件流。
冒泡型事件流:事件的传播是从最特定的事件目标到最不特定的事件目标。即从DOM树的叶子到根。
捕获型事件流:事件的传播是从最不特定的事件目标到最特定的事件目标。即从DOM树的根到叶子。
事件捕获的思想就是不太具体的节点应该更早接收到事件,而最具体的节点最后接收到事件。
DOM标准采用捕获+冒泡。两种事件流都会触发DOM的所有对象,从document对象开始,也在document对象结束。
DOM标准规定事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。如图:
事件捕获阶段:实际目标(div)在捕获阶段不会接收事件。事件从document到html再到body就停止了。上图中为1~3。
处于目标阶段:事件在目标上发生并处理。但是事件处理会被看成是冒泡阶段的一部分。
冒泡阶段:事件又传播回文档。
注意:
- 尽管“DOM2级事件”标准规范明确规定事件捕获阶段不会涉及事件目标,但是在IE9、Safari、Chrome、Firefox和Opera9.5及更高版本都会在捕获阶段触发事件对象上的事件。结果,就是有两次机会在目标对象上面操作事件。
- 并非所有的事件都会经过冒泡阶段 。所有的事件都要经过捕获阶段和处于目标阶段,但是有些事件会跳过冒泡阶段:如,获得输入焦点的focus事件和失去输入焦点的blur事件。
示例:
<body>
<div id="firstDiv">
<div id="secondDiv">
<button id="button1" class="clickMe">Click me</button>
</div>
</div>
<script>
var firstDiv = document.getElementById("firstDiv");
var secondDiv = document.getElementById("secondDiv");
var btn = document.getElementById("button1");
firstDiv .addEventListener('click',function(e){
alert("firstDiv的click事件在冒泡阶段被触发,id="+this.id);
},false);
firstDiv .addEventListener('click',function(e){
alert("firstDiv的click事件在捕获阶段被触发,id="+this.id);
},true);
secondDiv.addEventListener('click',function(e){
alert("secondDiv的click事件在冒泡阶段被触发,id="+this.id);
},false);
secondDiv.addEventListener('click',function(e){
alert("secondDiv的click事件在捕获阶段被触发,id="+this.id);
},true);
btn.addEventListener('click',function(e){
alert("button1的click事件在捕获阶段被触发,id="+this.id);
},true);
btn.addEventListener('click',function(e){
alert("button1的click事件在冒泡阶段被触发,id="+this.id);
},false);
</script>
</body>
虽然两次机会在目标对象上面操作事件,但其执行顺序却与代码的顺序相关,并非按照先捕获后冒泡的顺序。
3 事件处理程序##
事件就是用户或浏览器自身执行的某种动作。比如click,mouseup,keydown,mouseover等都是事件的名字。而响应某个事件的函数就叫事件处理程序(事件监听器),事件处理程序以on开头,比如click的事件处理程序就是onclick。
DOM 0级事件处理程序
DOM 0级事件处理程序:把一个函数赋值给一个事件的处理程序属性。
例如:
<input type="button" value="按钮" id="btn"/>
var oBtn=document.getElementById('btn'); //获得oBtn按钮对象
oBtn.onclick //给oBtn添加onclick属性,属性又触发了一个事件处理程序
oBtn.onclick=function(){ } //添加匿名函数
oBtn.onclick=null //删除onclick属性
DOM 2级事件处理程序
DOM 2级事件定义了两个方法,用于指定和删除事件处理程序的操作。addEventListener()和removeEventListener()。需要三个参数:事件名称,要分配的函数和处理函数是用于冒泡阶段(false)还是捕获阶段(true),默认为冒泡阶段false。
4 事件代理
事件代理的原理用到的就是事件冒泡和目标元素,把事件处理器添加到父元素,等待子元素事件冒泡,并且父元素能够通过target
(IE为srcElement
)判断是哪个子元素,从而做相应处理。
示例:
<body>
<ul id="color-list">
<li>red</li>
<li>orange</li>
<li>yellow</li>
<li>green</li>
<li>blue</li>
<li>indigo</li>
<li>purple</li>
</ul>
<script>
(function(){
var colorList=document.getElementById("color-list");
colorList.addEventListener('click',showColor,false);
function showColor(e)
{
e=e||window.event;
var targetElement=e.target||e.srcElement;
if(targetElement.nodeName.toLowerCase()==="li"){
alert(targetElement.innerHTML);
}
}
})();
</script>
</body>
事件代理的好处
- 将多个事件处理器减少到一个,因为事件处理器要驻留内存,这样就提高了性能。想象如果有一个100行的表格,对比传统的为每个单元格绑定事件处理器的方式和事件代理(即table上添加一个事件处理器),不难得出结论,事件代理确实避免了一些潜在的风险,提高了性能。
- DOM更新无需重新绑定事件处理器,因为事件代理对不同子元素可采用不同处理方法。如果新增其他子元素(a,span,div等),直接修改事件代理的事件处理函数即可,不需要重新绑定处理器,不需要再次循环遍历。