事件
事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。
事件通常与函数结合使用,函数不会在事件发生前被执行!
事件捕获
事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。
事件捕获的用意在于在事件到达预定目标之前就捕获它。
以下列HTML结构为例,说明事件冒泡、事件捕获及事件流。
// 示例一
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>
<button>点一下啦</button>
</div>
</body>
</html>
如果单击了页面中的button元素,那么这个click事件首先由document捕获,然后沿DOM树向下传播,一直传播到事件的实际目标的上一个元素。
[注意]IE9、Firefox、Chrome、Safari等现代浏览器都支持事件捕获,但是从window对象开始捕获。
为各dom元素绑定click事件:
// Internet Explorer 8 及更早IE版本不支持 addEventListener() 方法
// addEventListener有第三个参数useCapture,其作用是指定事件是否在捕获或冒泡阶段执行。
// 可选值为:true(捕获),false(默认、冒泡)
// 示例二
<script>
document.querySelector('button').addEventListener('click', captureBtn, true)
document.querySelector('div').addEventListener('click', captureDiv, true)
document.body.addEventListener('click', captureBody, true)
document.documentElement.addEventListener('click', captureHtml, true)
document.addEventListener('click', captureDocument, true)
window.addEventListener('click', captureWindow, true)
function captureBtn(){console.log('capture button')}
function captureDiv(){console.log('capture button')}
function captureBody(){console.log('capture button')}
function captureHtml(){console.log('capture button')}
function captureDocument(){console.log('capture button')}
function captureWindow(){console.log('capture button')}
</script>
点击button,可见打印结果如下:
事件冒泡
IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
同样以示例一的结构说明事件冒泡。
如果单击了页面中的button元素,那么这个click事件沿DOM树向上传播,在每一级节点上都会发生。
[注意]所有现代浏览器都支持事件冒泡,但在具体实现在还是有一些差别。IE9、Firefox、Chrome、Safari将事件一直冒泡到window对象,IE8以下事件冒泡到document。
为各dom元素绑定click事件:
// 示例三
<script>
document.querySelector('button').addEventListener('click', bubbleBtn)
document.querySelector('div').addEventListener('click', bubbleDiv)
document.body.addEventListener('click', bubbleBody)
document.documentElement.addEventListener('click', bubbleHtml)
document.addEventListener('click', bubbleDocument)
window.addEventListener('click', bubbleWindow)
function bubbleBtn(){console.log('bubble button')}
function bubbleDiv(){console.log('bubble button')}
function bubbleBody(){console.log('bubble button')}
function bubbleHtml(){console.log('bubble button')}
function bubbleDocument(){console.log('bubble button')}
function bubbleWindow(){console.log('bubble button')}
</script>
点击button,可见打印结果为:
事件流
DOM2级事件规定的事件流包括三个阶段:
- 事件捕获阶段(capture phase):实际目标button在捕获阶段不会接收事件。也就是在捕获阶段,事件从window到div就停止了。
- 处于目标阶段(target phase):事件在button上发生并处理。但是事件处理会被看成是冒泡阶段的一部分。
- 事件冒泡阶段(bubbling phase):事件又传播回Document对象。
若将示例二、三中的事件监听全部加上,点击button,打印结果如下:
但实际上,由以上打印可看出:
- 尽管“DOM2级事件”标准规范明确规定事件捕获阶段不会涉及事件目标,但是在IE9、Safari、Chrome、Firefox和Opera9.5及更高版本都会在捕获阶段触发事件对象上的事件。结果就是,我们有两次机会在目标对象上面操作事件。
- 并非所有的事件都会经过冒泡阶段 。所有的事件都要经过捕获阶段和处于目标阶段,但是有些事件会跳过冒泡阶段。
梳理一下哪些事件不能冒泡
UI事件
- load
- unload
- scroll
- resize
焦点事件
- blur
- focus
鼠标事件
- mouseleave
- mouseenter