javascript事件与事件委托

事件:

事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。javascript与HTML之间的交互就是通过事件来实现的。观察者模型:使用侦听器或处理程序要预定时间,以便时间发生时执行相应的代码,这种模型支持页面的行为(javascript)与页面的外观(HTML和CSS)之间的松散耦合。

事件监听器与处理程序:

  • 响应某个事件的函数就叫做事件处理程序或事件监听器。在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息,包括导致事件的元素,事件 的类型以及其他与特定事件相关的信息,这个对象不需要定义,可以直接使用。
  • 事件处理程序:事件处理程序的名字以on开头,类似click、load、和 mouseover都是事件的名字,因此,click的事件处理程序就是onclick。案例如下:onclick就是事件处理程序
1
2
3
4
5
6
<div id="child" style="width: 90px;height:90px;background-color: cyan;margin: 0 auto;" onclick="showMessage(event)" >孩子</div>
<script type="text/javascript">
function showMessage(event){
alert(event.target.id);
}
</script>
  • 事件监听器:例子如下:child.addEventListener("click",function (),true)是将指定的监听器注册到child上,当该对象触发指定的事件时,指定的回调函数就会被执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="child" style="width: 90px;height:90px;background-color: cyan;margin: 0 auto;" >孩子</div>
<script type="text/javascript">
let child = document.getElementById("child");
/**
* 事件监听器
* addEventListener(eventName , dealEventFunc , boolean)
* eventName: 要处理的事件名
* dealEventFunc: 作为事件处理程序的函数
* boolean: true表捕获事件,false表冒泡事件
* */
// 捕获事件流
child.addEventListener("click",function () {
//取消冒泡
//event.stopPropagation(); //w3c
//event.cancelBubble = true; //IE
console.log("捕获事件流:click child!");
},true);
//冒泡事件流
child.addEventListener("click",function () {
console.log("冒泡事件流:click child!");
},false);
</script>

事件流:

  • 构建一棵dom树如下:

image.png

  1. IE事件流:事件冒泡流。

事件开始时由最具体的元素(文档中嵌到层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。

image.png

  • 点击child<div>,显示如下:

image.png

  • 点击parent<div>,显示如下:

image.png

  1. NetSpace事件流:事件捕获流。

事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件,事件捕获的用意在于在事件到达预定目标之前捕获他。

image.png

  • 点击child<div>,显示如下:

image.png

  • 点击parent<div>,显示如下:

image.png

  1. DOM事件流:包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。

DOM2级事件规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会,然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

下图,捕获阶段:1,2,3,4

处于目标阶段在事件处理中被看成是冒泡阶段的一部分

然后冒泡事件发生:5,6,7,8,9

image.png

阻止事件冒泡

1
2
event.stopPropagation();          //w3c
event.cancelBubble = true; //IE

image.png

事件委托

  • 内存和性能:在javascript中,添加到页面上的时间处理程序数量将直接影响到页面的整体运行性能。导致这一问题的原因是多方面的,首先,每个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
  • 事件委托:事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。使整个页面只有少量的函数,从而优化页面性能。实例代码如下:
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
<!DOCTYPE html>
<html lang="en" id="html" onclick="clickDiv(event)">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body id="body">
<div id="grandparent" style="width: 270px;height:270px;background-color: blue; margin: 0 auto; text-align: center;line-height: 3">祖父
<div id="parent" style="width: 180px;height:180px;background-color: cadetblue;margin: 0 auto;">父亲
<div id="child" style="width: 90px;height:90px;background-color: cyan;margin: 0 auto;" >孩子
</div>
</div>
</div>
</body>
<script type="text/javascript">
let child = document.getElementById("child");
let parent = document.getElementById("parent");
let grandparent = document.getElementById("grandparent");
let body = document.getElementById("body");
let html = document.getElementById("html");
function clickDiv(event){
switch (event.target.id){
case 'child':console.log("click child!");break;
case 'parent':console.log("click parent!");break;
case 'grandparent':console.log("click grandparent!");break;
case 'body':console.log("click body!");break;
case 'html':console.log("click html!");break;
}
}
</script>
</html>
  • <html>标签中添加了onclick(event)事件处理程序。由于其他标签都是<html>的子节点,而且他们的事件会冒泡,所以单击事件终会被这个函数处理,点击各个点的输出情况如下所示。与给每个节点加一个处理程序相比,这种方法需要占用的内存更少,只添加了一个处理程序,虽然对用户来说最终结果都相同,但这种技术需要占用的内存更少,所有用到按钮的事件都适合采用事件委托技术

image.png

  • 如果可行的话,也可以考虑为document对象添加一个事件处理程序,用以处理页面上发生的某种特定类型的事件,这样做与传统的做法相比具有如下优点:
    1. document对象很快就可以访问,而且可以在生命周期的任何时间点上为它添加事件处理程序。‘
    2. 在页面中设置事件处理程序所需的时间更少,只添加体格事件处理程序所需的DOM引用更少,所花的时间也更少。
    3. 真个页面占用的内存空间更少,能够提升整体性能。
  • 整个demo代码