JavaScript 与 html 之间的交互是通过事件实现的. 事件, 就是文档或浏览器窗口中发生的一些特定的交互瞬间.
事件流
从页面中接收事件的顺序称为事件流.
IE --> 事件冒泡流
Netscape --> 事件捕获流
查看源码: DOM2 事件 - 捕获 - 冒泡 https://github.com/weiqinl/JavaScript/blob/master/第13章 事件/DOM2-eventCapturing-eventBubbling.html
事件冒泡
IE 的事件流叫做事件冒泡 (event bubbling), 即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点) 接收, 然后逐级向上传播到较为不具体的节点(文档).
我们先来个简单的例子, 这是 HTML 结构
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <title>js 事件流</title>
- </head>
- <body>
- <div id="div">
- 我是 div
- </div>
- </body>
- </html>
有一个 div 元素. 如果我们点击 < div > 元素, 那么这个 click 事件的顺序会怎么呢?
我们给几个元素都添加监听事件,
element.addEventListener(event, function, useCapture)
参数说明:
event 字符串. 指定事件名, 比如 click,mouseenter,mouseleave
function 函数. 指定要事件触发时执行的函数.
useCaption 布尔值. 指定事件是否在捕获或冒泡阶段执行. 默认值是 false, 即事件在冒泡阶段执行. true, 在捕获阶段执行
- var div = document.getElementById('div')
- var body = document.body
- var html = document.documentElement
- div.addEventListener('click', function () {
- console.log('div 标签')
- }, false)
- body.addEventListener('click', function () {
- console.log('body')
- }, false)
- html.addEventListener('click', function () {
- console.log('html')
- }, false)
- document.addEventListener('click', function () {
- console.log('document')
- }, false)
然后点击 < div > 元素, 查看控制台输出,
由上面的输出结果可以看出, 这个 click 事件会按照如下顺序传播:
<div> ---> <body>---> <html> --->document
也就是说, click 事件首先发生在目标元素, 然后, click 事件沿着 DOM 树向上传播到 document 对象. 这就是事件冒泡.
所有现代浏览器都支持事件冒泡. IE9,Firefox,Chrome 和 Safari 则将事件一直冒泡到 window 对象.
如果, 我们在每个 DOM 元素上都设置监听事件, 会得到的事件的传播顺序是:
<div> ---><body> ---> <html> --->document ---> window
事件捕获
Netscape Communicator 团队提出的另一种事件流叫做事件捕获(event capturing). 事件捕获是不太具体的节点应该更早接收到事件, 而最具体的节点应该最后接收到事件. 事件捕获的用意在于在事件到达预期目标之前捕获它.
我们还是可以看刚才的例子, DOM 结构不变, 我们修改下监听事件
- var div = document.getElementById('div')
- var body = document.body
- var html = document.documentElement
- div.addEventListener('click', function () {
- console.log('div 标签')
- }, true)
- body.addEventListener('click', function () {
- console.log('body')
- }, true)
- html.addEventListener('click', function () {
- console.log('html')
- }, true)
- document.addEventListener('click', function () {
- console.log('document')
- }, true)
之后, 还是点击 < div > 元素, 得到的结果
从上面例子可以看出, 事件走向: document ---><html> ---> <body> ---><div>
也就是说, 在事件捕获过程中, document 对象首先接收到 click 事件, 然后事件沿着 DOM 树依次向下, 一直传播到事件的实际目标, 即 < div > 元素. 这就是事件捕获. 与事件冒泡过程, 截然相反.
尽管 "DOM2 级事件" 规范要求事件应该从 document 对象开始传播, 但是现代浏览器大部分都是从 window 对象开始捕获事件的.
如果, 我们在每个 DOM 元素上都设置监听事件, 会得到的事件的传播顺序是:
window ---> document ---><html> ---> <body> ---><div>
由于在老版本的浏览器中不支持, 因此事件捕获用的人比较少, 除非在特殊需要的时候才使用.
DOM 事件流
"DOM2 级事件" 规定的事件流包括三个阶段: 事件捕获阶段, 处于目标阶段和事件冒泡阶段.
首先发生的是事件捕获, 为截获事件提供了机会. 然后是实际的目标接收到事件. 最后一个阶段是冒泡阶段, 可以在这个阶段对事件作出响应.
事件捕获阶段: 在 DOM 事件流中, 实际的目标 (<div > 元素) 在捕获阶段不会接收到事件. 这意味着在捕获阶段, 事件从 document 到 < html > 再到 < body > 后就停止了.
处于目标阶段: 事件在 < div > 上发生, 并在事件处理中被看成冒泡阶段的一部分.
事件冒泡阶段: 冒泡阶段发生, 事件又传播回文档.
虽然,"DOM2 级事件" 规范明确要求捕获阶段不会涉及事件目标, 但是 IE9 及以上的现代浏览器都会在捕获阶段触发事件目标对象上的事件. 结果, 就是有两个机会在目标对象上面操作事件, 也就是说上图中的步骤 4, 既可以在捕获阶段发生, 也可以在冒泡阶段发生.
IE8 级更早版本不支持 DOM 事件流, 现代浏览器都支持 DOM 事件流.
事件流的应用
事件流比较典型应用是事件委托. 事件委托利用了事件冒泡, 只指定一个事件处理程序, 就可以管理一类型的所有事件.
我们查看一个常用例子, 这是一个无序列表的 DOM 结构:
<ul>
<li id="li1">我是第 1 个 li</li>
<li id="li2">我是第 2 个 li</li>
<li id="li3">我是第 3 个 li</li>
</ul>
我们的需求是, 点击不同列, 输出不同的消息.
第一种做法: 给每个 < li > 添加点击事件, 这样能分别处理事件, 展示不同的内容.
- document.getElementById('li1').addEventListener('click', function(e) {
- console.log('我是第一个 li')
- }, false)
- document.getElementById('li2').addEventListener('click', function(e) {
- console.log('我是第 2 个 li')
- }, false)
- document.getElementById('li3').addEventListener('click', function(e) {
- console.log('我是第 3 个 li')
- }, false)
单击每一个 < li>, 会输出对应的内容.
第二种做法: 给 < li > 元素的父元素 < ul > 添加一个处理事件,
- document.querySelector('ul').addEventListener('click', function (e) {
- console.log(e.target.innerText)
- }, false)
单击每一个 < li>, 会展示不同的 < li > 中文本元素内容.
在这段代码中, 我们只为 < ul > 元素添加了一个 onclick 事件处理程序. 由于所有 < li > 都是 < ul > 元素的子节点, 而且它们的事件会冒泡, 所以单击事件最终会被这个函数处理.
以上两种方式, 第二种所具有的优势:
事前消耗更低. 因为只取得了一个 DOM 元素, 只添加了一个事件处理程序.
占用的内存更少. 每个函数都是对象, 都会占用内存.
性能更优. 内存中的对象越多, 性能就越差.
如果以后要增减 < li > 元素, 也不用修改事件方法, 可以获取相同的处理结果.
所以, 比较推荐使用第二种方式.
最适合采用事件委托技术的事件包括 click,mousedown,mouseup,keydown,keyup 和 keypress. 虽然 mouseover 和 mouseout 事件也冒泡, 但要适当处理它们并不容易, 而且经常要计算元素的位置.
来源: https://www.cnblogs.com/weiqinl/p/9460711.html