基础:帮你总结的JavaScript_事件相关知识和资料
1. 事件是如何触发JS代码的?
事件发生以后,就会有一些JS代码被执行,这个过程又叫“事件处理”(Event Handling)。它分为三个步骤:
-
选择要响应某事件的DOM节点
-
指明要响应的事件,这又叫把事件绑定(binding)到DOM树
-
设置要触发的代码——通常是一个函数。
-
2. 事件绑定
把事件绑定到 DOM Nodes 上,有三种方法:
-
HTML Event Handler:已过时,不要再用了,但你需要知道。
-
DOM Event Handler:DOM1的方法,浏览器支持广泛,唯一的缺点是只能给一个事件指定一个函数,所以如果有两个脚本同事在处理某个事件,那么它们可能不会按照预想的去工作。
-
Event listener:DOM2的方法,能够给一个事件指定多个函数。
-
2.1. HTML Event Handler
<input type="text" id="username" onblur="checkUserName()" /> <div id="feedback"></div> <script> function checkUserName(){ var elMsg = document.getElementById("feedback"); var elUserName = document.getElementById("username"); if(elUserName.value.length < 5){ elMsg.textContent = "Username must be 5 characters or more!"; } } </script>
以上代码响应的是 blur 事件,即元素失去焦点。用户填写完用户名,就会点击别的输入框,这时候用户名所在的<input>元素就失去了焦点,blur 事件发生了。
为了给 <input> 元素绑定 blur,HTML Event Handler 的做法是添加一个 onblur 属性,其值为一个函数,而这个函数定义在 JavaScript 脚本中。
2.2. DOM Event Handler

如图,有几个关键点:
-
事件名称都以 on 开头
-
如果是命名函数,函数名后没有括号;也可以使用匿名函数
函数名后不能有括号是因为我们的目的是把函数当做变量赋值给元素的事件属性,如果加了括号,就成了把函数的运行结果赋值给元素的事件属性。
-
上节的代码用这种方法写就是:
<input type="text" id="username" /> <div id="feedback"></div> <script> //定义函数 function checkUserName(){ var elMsg = document.getElementById("feedback"); if(this.value.length < 5){ elMsg.textContent = "Username must be 5 characters or more!"; } } //选择元素 var elUserName = document.getElementById("username"); //绑定事件 elUserName.onblur = checkUsreName; </script>
如果在DOM Event Handler中使用命名函数,那么该如何给这个函数传参呢?因为它不能有括号!答案是:
//为了给命名函数传参,我们把它放在一个匿名函数里 element.onblur = function{ checkUserName(5); }
在下一节 Event Listener 中也是这么干的。
2.3. Event Listener

如图,有几个关键点:
-
在元素的 addEventListener 方法中,事件名称用引号括起来
-
要调用的函数应该是命名函数,而且在绑定时不能有括号
-
最后一个参数通常设置为 false,这是用来指示事件流的
-
如果想要把这个事件从元素上“解绑”,可以使用 removeEventLitener,参数一样
-
上节代码用这种方法写就是:
<input type="text" id="username" />
<div id="feedback"></div>
<script>
//定义函数
function checkUserName(){
var elMsg = document.getElementById("feedback");
if(this.value.length < 5){
elMsg.textContent = "Username must be 5 characters or more!";
}
}
//选择元素
var elUserName = document.getElementById("username");
//绑定事件
elUserName.addEventListener("blur", checkUserName, false);
</script>
给命名函数传参:
//绑定事件 elUserName.addEventListener("blur", function(){ checkUserName(5); }, false);
2.4. IE5 ~ IE8兼容
IE5 ~IE8不支持 addEventListener() 方法,它们使用的是 attachEvent()。为了支持这两个老浏览器,可以采用这样的策略:
先检测目标元素有没有 addEventListener() 这个方法,如果没有,就用 attachEvent() 方法。
attachEvent的语法是这样的:
//事件名要以 on 开头 element.attachEvent(eventNameWithOn, callback);
所以兼容的写法是这样的:
var el = document.getElementById("username"); if(el.addEventListener){ el.addEventListener("blur", checkUserName, false; } else{ el.attachEvent("onblur", checkUserName); }
3. 事件流
事件Handler或者Listener被绑定到目标元素,其实也同时绑定到了其祖先(直到<html>元素)。那么事件的发生是从目标元素一直到<html>元素,还是从<html>元素一直到目标元素呢?这个顺序就是事件流(event flow)。

如图,事件流有两种,事件冒泡(Event Bubbling)是从目标元素一层层到父级元素;事件捕获(Event Capturing)是从最顶层父级元素一直到目标元素。
所有浏览器默认使用事件冒泡,IE8及之前的IE版本都不支持事件捕获。addEventListener()方法的最后一个参数就是在指定用事件冒泡还是事件捕获,通常都是 false——事件冒泡。
3.1. 事件流为何很重要?
正因为事件不是仅仅发生在目标元素上,所以当目标元素及其祖先或后代元素都有事件被绑定时,事件流才显得很重要。为什么这么说呢?如果我在一个<div>里包含了一个<video>,我希望一点击视频封面就开始播放视频,就像这样:

我的脚本是这么写的:
video.onclick = function(){ video.play(); };
【关于play()函数】
这没什么问题,视频肯定能正常播放。但是我无意间在另一个脚本中给<video>的父级元素<div>设置了这么一段程序:
//下面的videoBox指的就是那个div videoBox.onclick = function() { videoBox.setAttribute('hidden', 'true'); };
现在,我一点击视频封面,视频会动一下,但是立马就消失了。我费尽心思去解决这个问题,直到懂得了事件冒泡和事件捕获才明白为什么——
首先要知道,如果使用 DOM Handler,就像以上程序,那么浏览其就会默认采用事件冒泡机制。
当我点击视频时,浏览器检测到<video>元素上发生了 click 事件,开始运行 play() 函数。紧接着,浏览器开始检测父级元素,发现click事件同样发生在了 <div>上,于是该盒子的hidden属性被设置为true。所以整个盒子包括视频都不见了。其实检测还不会停止,直到检测到<html>元素。
如果在以上程序中使用了Event Listener,那么可以指定采用冒泡还是捕获。如果指定为捕获(true),只要一点击视频,那么绑定在<div>上的代码会被先执行,盒子消失得更快。
3.2. 如何解决这个问题?
要解决这个问题,最好是阻止事件向目标元素的祖先或者后代传播。因此,可以使用事件对象的 stopPropagation() 方法:
video.onclick = function(event_object) { event_object.stopPropagation(); video.play(); };
在以上代码中,参数event_object指事件对象,其实参数名称无所谓啦,因为JavaScript中函数的参数名只是为了提供便利,但不是必须的。根据惯例,使用 event、e 会比较好。
在IE5-IE8中,事件对象不会自动传递到监听器中,而是 window 对象的一个属性,所以以上代码有了兼容写法:
video.onclick = function(event_object){ if(!event_object){ event_object = window.event; } event_object.stopPropagation(); video.play(); }
其实事件冒泡和事件捕获只不过是处理事情的机制,在有些情况下,你会需要它们,所以记得用 addEventListener()哦!
4.事件委托
现在有一个父元素<div>,它有很多子元素,比如很多按钮、长列表、表格中的单元格。如果想给这些子元素绑定事件,那一定很麻烦,而且给大量元素绑定事件会拖慢整个页面。
根据事件捕获机制,事件会从父元素传播到子元素,所以我们只需要把事件绑定给父元素,然后采用事件捕获机制。这就叫事件委托——它能让你少用一些监听器。

浙公网安备 33010602011771号