JavaScript学习笔记(十五) 事件模型
0、DOM 标准
在开始学习 JavaScript 事件模型前,我们首先来了解一下什么是 DOM(Document Object Model)
简单来说,DOM 是 W3C 定义的访问 HTML 和 XML 文档的标准
按照不同的发展阶段,分为不同的级别,分别是 DOM0、DOM1、DOM2、DOM3
DOM0、DOM2、DOM3 都有定义与事件相关的内容,但是 DOM1 没有定义与事件相关的内容
1、事件模型
(1)事件捕获
事件从文档对象上开始,然后往下传递,直到目标对象(从父到子),低版本浏览器不支持事件捕获
<!DOCTYPE html>
<html>
<head>
<title>事件捕获</title>
</head>
<body>
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Cherry</li>
</ul>
</body>
<script>
let html = document.documentElement
html.addEventListener('click', () => { console.log('html') }, true)
let body = document.body
body.addEventListener('click', () => { console.log('body') }, true)
let uls = document.getElementsByTagName('ul')
for (let ul of uls) { ul.addEventListener('click', () => { console.log('ul') }, true) }
let lis = document.getElementsByTagName('li')
for (let li of lis) { li.addEventListener('click', () => { console.log('li') }, true) }
// 点击列表项,打印结果为:html body ul li
</script>
</html>
(2)事件冒泡
事件从目标对象上开始,然后往上传递,直到文档对象(从子到父),所有的浏览器都支持事件冒泡
<!DOCTYPE html>
<html>
<head>
<title>事件冒泡</title>
</head>
<body>
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Cherry</li>
</ul>
</body>
<script>
let html = document.documentElement
html.addEventListener('click', () => { console.log('html') }, false)
let body = document.body
body.addEventListener('click', () => { console.log('body') }, false)
let uls = document.getElementsByTagName('ul')
for (let ul of uls) { ul.addEventListener('click', () => { console.log('ul') }, false) }
let lis = document.getElementsByTagName('li')
for (let li of lis) { li.addEventListener('click', () => { console.log('li') }, false) }
// 点击列表项,打印结果为:li ul body html
</script>
</html>
(3)事件流模型
DOM2 定义了事件流模型,这是目前广泛使用的 JavaScript 事件模型,规定事件传递先捕获后冒泡
<!DOCTYPE html>
<html>
<head>
<title>事件流模型</title>
</head>
<body>
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Cherry</li>
</ul>
</body>
<script>
let html = document.documentElement
html.addEventListener('click', () => { console.log('Chtml') }, true)
html.addEventListener('click', () => { console.log('Bhtml') }, false)
let body = document.body
body.addEventListener('click', () => { console.log('Cbody') }, true)
body.addEventListener('click', () => { console.log('Bbody') }, false)
let uls = document.getElementsByTagName('ul')
for (let ul of uls) { ul.addEventListener('click', () => { console.log('Cul') }, true) }
for (let ul of uls) { ul.addEventListener('click', () => { console.log('Bul') }, false) }
let lis = document.getElementsByTagName('li')
for (let li of lis) { li.addEventListener('click', () => { console.log('Cli') }, true) }
for (let li of lis) { li.addEventListener('click', () => { console.log('Bli') }, false) }
// 点击列表项,打印结果为:Chtml Cbody Cul Cli Bli Bul Bbody Bhtml
</script>
</html>
2、事件处理程序
(1)DOM0 事件处理程序
通过事件监听函数指定事件处理程序,事件处理程序在冒泡阶段执行
<button id="submit">提交</button>
<script>
var button = document.getElementById('submit')
// 对于 DOM0 事件处理函数,this 指向目标对象,这里 this 指向 button
var handleEvent = function(e) { console.log(this) }
// 添加事件处理函数 button.onclick = handleEvent
// 删除事件处理函数 button.onclick = null
button.onclick = handleEvent
</script>
DOM0 事件处理程序,同一个事件只能绑定一个事件处理函数,后面绑定的处理函数会覆盖前面绑定的处理函数
<button id="submit">提交</button>
<script>
var button = document.getElementById('submit')
var sayHello = function(e) { console.log('Hello') }
var sayGoodbye = function(e) { console.log('Goodbye') }
button.onclick = sayHello
button.onclick = sayGoodbye
// 只会打印 Goodbye
</script>
(2)DOM2 事件处理程序
通过 addEventListener()
添加事件处理程序,通过 removeEventListener()
删除事件处理程序
它们都接收三个参数,分别是事件名称、事件处理函数以及一个布尔类型的值,这个布尔值默认为 false
如果布尔值为 false,则在冒泡阶段执行处理函数,如果布尔值为 true,则在捕获阶段执行处理函数
<button id="submit">提交</button>
<script>
var button = document.getElementById('submit')
// 对于 DOM2 事件处理函数,this 指向目标对象,这里 this 指向 button
var handleEvent = function(e) { console.log(this) }
// 添加事件处理函数 button.addEventListener('click', handleEvent)
// 删除事件处理函数 button.removeEventListener('click', handleEvent)
button.addEventListener('click', handleEvent)
</script>
DOM2 事件处理程序,同一个事件可以绑定多个事件处理函数,先绑定先执行
<button id="submit">提交</button>
<script>
var button = document.getElementById('submit')
var sayHello = function(e) { console.log('Hello') }
var sayGoodbye = function(e) { console.log('Goodbye') }
button.addEventListener('click', sayHello)
button.addEventListener('click', sayGoodbye)
// 先打印 Hello,再打印 Goodbye
</script>
(3)低版本 IE 事件处理程序(IE9 之前)
通过 attachEvent()
添加事件处理程序,通过 detachEvent()
删除事件处理程序
它们都接收两个参数,分别是事件监听函数名称以及事件处理函数,将在冒泡阶段执行处理函数
<button id="submit">提交</button>
<script>
var button = document.getElementById('submit')
// 对于低版本 IE 事件处理函数,this 指向全局对象,这里 this 指向 window
var handleEvent = function() { console.log(this) }
// 添加事件处理函数 button.attachEvent('onclick', handleEvent)
// 删除事件处理函数 button.detachEvent('onclick', handleEvent)
button.attachEvent('onclick', handleEvent)
</script>
低版本 IE 事件处理程序,同一个事件可以绑定多个事件处理函数,先绑定后执行
<button id="submit">提交</button>
<script>
var button = document.getElementById('submit')
var sayHello = function() { console.log('Hello') }
var sayGoodbye = function() { console.log('Goodbye') }
button.attachEvent('onclick', sayHello)
button.attachEvent('onclick', sayGoodbye)
// 先打印 Goodbye,再打印 Hello
</script>
3、事件类型
HTML 与 JavaScript 通过事件进行交互,常见的事件如下:
(1)鼠标事件
mousedown
:在按下鼠标按键时触发mouseup
:在松开鼠标按键时触发click
:在点击鼠标时触发dblclick
:在双击鼠标时触发mouseenter
:在鼠标移入特定范围时触发mouseleave
:在鼠标移出特定范围时触发mousemove
:鼠标在特定范围内移动时触发mouseover
:鼠标移到某元素上触发mouseout
:鼠标从某元素移开触发
(2)键盘事件
keydown
:在按下键盘按键时触发keyup
:在松开键盘按键时触发keypress
:在按住键盘按键时触发
(3)UI 事件
load
:在文档或图像加载完成后触发unload
:在文档或图像销毁完成后触发error
:加载文档或图像时发生错误abort
:加载文档或图像时出现中断scroll
:在滚动滚动条时触发resize
:调整窗口大小时触发
(4)焦点事件
focus
:在元素获得焦点时触发,事件不冒泡blur
:在元素失去焦点时触发,事件不冒泡
(5)表单事件
select
:在被选定时触发change
:在被修改时触发submit
:在被提交时触发
4、事件对象
当一个事件被触发时,会产生一个事件对象,这个事件对象会隐式传入事件处理函数
因此,我们可以在事件处理函数中通过事件对象的属性和方法获取事件的相关信息
(1)事件对象的常用属性
type
:事件名称target
:触发事件的目标元素currentTarget
:触发事件的当前元素bubbles
:事件是否为冒泡类型cancelBubble
:是否取消冒泡行为cancelable
:是否能调用preventDefault()
方法取消默认行为defaultPrevented
:是否有调用preventDefault()
方法eventPhase
:事件传播的阶段timeStamp
:触发事件的时间clientX
:鼠标指针相对于浏览器页面的水平坐标
clientY
:鼠标指针相对于浏览器页面的垂直坐标screenX
:鼠标指针相对于屏幕的水平坐标screenY
:鼠标指针相对于屏幕的垂直坐标shiftKey
:“SHIFT” 键是否被按下ctrlKey
:“CTRL” 键是否被按下altKey
:“ALT” 键是否被按下metaKey
:“meta” 键是否被按下
(2)事件对象的常用方法
preventDefault
:阻止默认行为stopPropagation
:阻止事件传播
【 阅读更多 JavaScript 系列文章,请看 JavaScript学习笔记 】