事件
处理程序
传递事件源对象、参数、事件对象。
<button onclick="show(this,'param',event)">后盾人</button>
<script>
function show(...args) {
console.log(args)
}
</script>
DOM 绑定
<div id="app">houdunren.com</div>
<script>
const app = document.querySelector('#app')
app.onclick = function () {
this.style.color = 'red'
}
</script>
事件监听
通过上面的说明我们知道使用 HTML 与 DOM 绑定事件都有缺陷,建议使用新的事件监听绑定方式 addEventListener 操作事件
使用 addEventListener 添加事件处理程序有以下几个特点
- transtionend / DOMContentLoaded 等事件类型只能使用 addEventListener 处理
- 同一事件类型设置多个事件处理程序,按设置的顺序先后执行
- 也可以对未来添加的元素绑定事件
方法 | 说明 |
---|---|
addEventListener | 添加事件处理程序 |
removeEventListener | 移除事件处理程序 |
addEventListener 的参数说明如下
- 参数一事件类型
- 参数二事件处理程序
- 参数三为定制的选项,可传递 object 或 boolean 类型。
通过对象绑定
如果事件处理程序可以是对象,对象的 handleEvent 方法会做为事件处理程序执行。下面将元素的事件统一交由对象处理
<div id="app">houdunren.com</div>
<script>
const app = document.querySelector('#app')
class HD {
handleEvent(e) {
this[e.type](e)
}
click() {
console.log('单击事件')
}
mouseover() {
console.log('鼠标移动事件')
}
}
app.addEventListener('click', new HD())
app.addEventListener('mouseover', new HD())
</script>
事件选项
addEventListener 的第三个参数为定制的选项,可传递 object 或 boolean 类型
下面是传递对象时的说明
选项 | 可选参数 | |
---|---|---|
once | true/false | 只执行一次事件 |
capture | true/false | 事件是在捕获/冒泡哪个阶段执行,true:捕获阶段 false:冒泡阶段 |
passive | true/false | 声明事件里不会调用 preventDefault() ,可以减少系统默认行为的等待 |
下面使用 once:true 来指定事件只执行一次
<button id="app">houdunren.com</button>
<script>
const app = document.querySelector('#app')
app.addEventListener(
'click',
function () {
alert('only once')
},
{ once: true }
)
</script>
设置 { capture: true }
或直接设置第三个参数为 true 用来在捕获阶段执行事件
addEventListener 的第三个参数传递 true/false 和设置 {capture:true/false}是一样
<div id="app" style="background-color: red">
<button id="bt">houdunren.com</button>
</div>
<script>
const app = document.querySelector('#app')
const bt = document.querySelector('#bt')
app.addEventListener(
'click',
function () {
alert('这是div事件 ') // 设置 { capture: false } 或直接设置第三个参数为 false 用来在冒泡阶段执行事件
},
{ capture: true }
)
bt.addEventListener(
'click',
function () {
alert('这是按钮事件 ')
},
{ capture: true } //如果为false就是冒泡事件,会先触发按钮时间,在触发dive事件
)
</script>
事件对象
执行事件处理程序时,会产生当前事件相关信息的对象,即为事件对事。系统会自动做为参数传递给事件处理程序。
- 大部分浏览器将事件对象保存到 window.event 中
- 有些浏览器会将事件对象做为事件处理程序的参数传递
事件对象常用属性如下:
属性 | 说明 |
---|---|
type | 事件类型 |
target | 事件目标对象,冒泡方式时父级对象可以通过该属性找到在哪个子元素上最终执行事件 |
currentTarget | 当前执行事件的对象 |
timeStamp | 事件发生时间 |
x | 相对窗口的 X 坐标 |
y | 相对窗口的 Y 坐标 |
clientX | 相对窗口的 X 坐标 |
clientY | 相对窗口的 Y 坐标 |
screenX | 相对计算机屏幕的 X 坐标 |
screenY | 相对计算机屏幕的 Y 坐标 |
pageX | 相对于文档的 X 坐标 |
pageY | 相对于文档的 Y 坐标 |
offsetX | 相对于事件对象的 X 坐标 |
offsetY | 相对于事件对象的 Y 坐标 |
layerX | 相对于父级定位的 X 坐标 |
layerY | 相对于父级定位的 Y 坐标 |
altKey | 是否按了 alt 键 |
shiftKey | 是否按了 shift 键 |
metaKey | 是否按了媒体键 |
window.pageXOffset | 文档参考窗口水平滚动的距离 |
window.pageYOffset | 文档参考窗口垂直滚动的距离 |
冒泡捕获
冒泡行为
- 大部分事件都会冒泡,但像 focus 事件则不会
- event.target 可以在事件链中最底层的定义事件的对象,也就是触发事件的直接元素。
- event.currentTarget == this 即当前执行事件的对象,回调函数不能是箭头函数形式,否则this指向为window。
阻止冒泡
冒泡过程中的任何事件处理程序中,都可以执行 event.stopPropagation()
方法阻止继续进行冒泡传递
- event.stopPropagation() 用于阻止冒泡
- 如果同一类型事件绑定多个事件处理程序 event.stopPropagation() 只阻止当前的事件处理程序
- event.stopImmediatePropagation() 阻止事件冒泡并且阻止相同事件的其他事件处理程序被调用
下例中为 h2 的事件处理程序添加了阻止冒泡动作,将不会产生冒泡,也就不会执行父级中的事件处理程序了。
<style>
#app {
background: #34495e;
width: 300px;
padding: 30px;
}
#app h2 {
background-color: #f1c40f;
margin-right: -100px;
}
</style>
<div id="app">
<h2>内层</h2>
</div>
<script>
const app = document.querySelector('#app')
const h2 = document.querySelector('h2')
app.addEventListener('click', (event) => {
console.log(`event.currentTarget:${event.currentTarget.nodeName}`)
console.log(`event.target:${event.target.nodeName}`)
console.log('app event')
})
h2.addEventListener('click', (event) => {
event.stopPropagation()
console.log(`event.currentTarget:${event.currentTarget.nodeName}`)
console.log(`event.target:${event.target.nodeName}`)
console.log(`h2 event`)
})
h2.addEventListener('click', (event) => {
console.log('h2 的第二个事件处理程序')
})
</script>
以上代码如果将 event.stopPropagation() 替换为 event.stopImmediatePropagation() ,那么 h2 的其他同类型的事件处理程序将不执行,同时阻止冒泡。
事件捕获
事件执行顺序为 捕获 > 事件目标 > 冒泡,在向下传递到目标对象的过程即为事件捕获。事件捕获在实际使用中频率不高。
- 通过设置第三个参数为 true 或{ capture: true } 在捕获阶段执行事件处理程序
const main = document.querySelector('main')
const app = document.querySelector('#app')
const h2 = document.querySelector('h2')
main.addEventListener(
'click',
(event) => {
console.log('main event')
}
)
app.addEventListener(
'click',
(event) => {
console.log('app event')
},
{ capture: true }
)
h2.addEventListener('click', (event) => {
console.log(`h2 event`)
})
//执行顺序 app event->h2 event->main event
事件代理
借助冒泡思路,我们可以不为子元素设置事件,而将事件设置在父级。然后通过父级事件对象的 event.target 查找子元素,并对他做出处理。
- 这在为多个元素添加相同事件时很方便
- 会使添加事件变得非常容易,即便是新创建的子元素也同样可以绑定事件
下面是为父级 UL 设置事件来控制子元素 LI 的样式切换
<style>
.hd {
border: solid 2px #ddd;
background-color: red;
color: white;
}
</style>
<ul>
<li>houdunren.com</li>
<li>hdcms.com</li>
</ul>
<button>add</button>
<script>
'use strict'
const ul = document.querySelector('ul')
const button = document.querySelector('button')
ul.addEventListener('click', () => {
if (event.target.tagName === 'LI') event.target.classList.toggle('hd')
})
let li = document.createElement('li')
li.innerHTML = 'add'
button.addEventListener('click', () => {
ul.appendChild(li)
})
</script>
可以使用事件代理来共享事件处理程序,不用为每个元素单独绑定事件
<ul>
<li data-action="hidden">houdunren.com</li>
<li data-action="color" data-color="red">hdcms.com</li>
</ul>
<script>
class HD {
constructor(el) {
el.addEventListener('click', (e) => {
const action = e.target.dataset.action
this[action](e)
})
}
hidden() {
event.target.hidden = true
}
color() {
event.target.style.color = event.target.dataset.color
}
}
new HD(document.querySelector('ul'))
</script>
默认行为
JS 中有些对象会设置默认事件处理程序,比如 A 链接在点击时会进行跳转。
一般默认处理程序会在用户定义的处理程序后执行,所以我们可以在我们定义的事件处理中取消默认事件处理程序的执行。
- 使用 onclick 绑定的事件处理程序,return false 可以阻止默认行为
- 推荐使用
event.preventDefault()
阻止默认行为
下面阻止超链接的默认行为
<a href="https://www.houdunren.com">后盾人</a>
<script>
document.querySelector('a').addEventListener('click', () => {
event.preventDefault()
alert(event.target.innerText)
})
</script>
窗口文档
下面来学习针对窗口和文档的事件的处理。
事件类型
事件名 | 说明 |
---|---|
window.onload | 文档解析及外部资源加载后 |
DOMContentLoaded | 文档解析后执行,不需要等待图片/样式文件等外部资源加载,该事件只能通过 addEventListener 设置 |
window.beforeunload | 文档刷新或关闭时 |
window.unload | 文档卸载时 |
scroll | 页面滚动时 |
beforeunload
当浏览器窗口关闭或者刷新时,会触发 beforeunload 事件,可以取消关闭或刷新页面。
- 返回值为非空字符串时,有些浏览器会做为弹出的提示信息内容
- 部分浏览器使用 addEventListener 无法绑定事件
window.onbeforeunload = function (e) {
return '真的要离开吗?'
}
unload
window.unload 事件在文档资源被卸载时执行,在 beforeunload 后执行
- 不能执行 alert、confirm 等交互指令
- 发生错误也不会阻止页面关闭或刷新
window.addEventListener('unload', function (e) {
localStorage.setItem('name', 'houdunren')
})
鼠标事件
事件类型
针对鼠标操作的行为有多种事件类型
- 鼠标事件会触发在 Z-INDEX 层级最高的那个元素上
事件名 | 说明 |
---|---|
click | 鼠标单击事件,同时触发 mousedown/mouseup |
dblclick | 鼠标双击事件 |
contextmenu | 点击右键后显示的所在环境的菜单 |
mousedown | 鼠标按下 |
mouseup | 鼠标抬起时 |
mousemove | 鼠标移动时 |
mouseover | 鼠标移动时 |
mouseout | 鼠标从元素上离开时 |
mouseup | 鼠标抬起时 |
mouseenter | 鼠标移入时触发,不产生冒泡行为 |
mouseleave | 鼠标移出时触发,不产生冒泡行为 |
oncopy | 复制内容时触发 |
scroll | 元素滚动时,可以为元素设置 overflow:auto; 产生滚动条来测试 |
事件对象
鼠标事件产生的事件对象包含相对应的属性
属性 | 说明 |
---|---|
which | 执行 mousedown/mouseup 时,显示所按的键 1 左键,2 中键,3 右键 |
clientX | 相对窗口 X 坐标 |
clientY | 相对窗口 Y 坐标 |
pageX | 相对于文档的 X 坐标 |
pageY | 相对于文档的 Y 坐标 |
offsetX | 目标元素内部的 X 坐标 |
offsetY | 目标元素内部的 Y 坐标 |
altKey | 是否按了 alt 键 |
ctrlKey | 是否按了 ctlr 键 |
shiftKey | 是否按了 shift 键 |
metaKey | 是否按了媒体键 |
relatedTarget | mouseover 事件时从哪个元素来的,mouseout 事件时指要移动到的元素。当无来源(在自身上移动)或移动到窗口外时值为 null |
禁止复制
<body>
copy
<script>
document.addEventListener('copy', () => {
event.preventDefault()
alert('禁止复制内容')
})
</script>
</body>
键盘事件
针对键盘输入操作的行为有多种事件类型
事件名 | 说明 |
---|---|
keydown | 键盘按下时,一直按键不松开时 keydown 事件会重复触发 |
keyup | 按键抬起时 |
事件对象
键盘事件产生的事件对象包含相对应的属性
属性 | 说明 |
---|---|
keyCode | 返回键盘的 ASCII 字符数字 |
code | 按键码,字符以 Key 开始,数字以 Digit 开始,特殊字符有专属名子。左右 ALT 键字符不同。 不同布局的键盘值会不同 |
key | 按键的字符含义表示,大小写不同。不能区分左右 ALT 等。不同语言操作系统下值会不同 |
altKey | 是否按了 alt 键 |
ctrlKey | 是否按了 ctlr 键 |
shiftKey | 是否按了 shift 键 |
metaKey | 是否按了媒体键 |
表单事件
下面是可以用在表单上的事件类型
事件类型 | 说明 |
---|---|
focus | 获取焦点事件 |
blur | 失去焦点事件 |
element.focus() | 让元素强制获取焦点 |
element.blur() | 让元素失去焦点 |
change | 文本框在内容发生改变并失去焦点时触发,select/checkbox/radio 选项改变时触发事件 |
input | Input、textarea 或 select 元素的 value 被修改时,会触发 input 事件。而 change 是鼠标离开后或选择一个不同的 option 时触发。 |
submit | 提交表单 |