MDN扫盲之web事件
web事件列表
MDN文档:
https://developer.mozilla.org/zh-CN/docs/Web/Events
资源事件
error
资源加载失败时触发。
比如我们现在加载一张不存在的图片,当加载失败时,打印提示资源加载失败:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<img src="../222.png" alt="" class="_img">
<script>
let _img = document.querySelector('._img')
_img.addEventListener('error', function() {
confirm(this.src + '资源加载失败了!')
})
</script>
</body>
</html>
可以看到弹框提示:
beforeunload
当浏览器窗口关闭或者刷新时,会触发beforeunload事件
在该事件触发时,可以关闭定时器,或者给用户一些确认或提示等。
网络事件
navigator.onLine
当浏览器可以访问网络时,该值为true,否则为false。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
console.log(navigator.onLine)
</script>
</body>
</html>
控制台输出:
打印事件
beforeprint
: 打印机已经就绪时触发
afterprint
: 打印机关闭时触发
视图事件
fullscreenchange
: 当浏览器进入或离开全屏时触发.
resize
文档视图调整大小时会触发 resize 事件。
document.body.clientWidth
是文档可视区宽度
window.innerWidth
是设备屏幕宽度
当它们的宽度发生变化时,会触发resize事件。
同时,这两个宽度还有一定的联系。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
</style>
</head>
<body>
<script>
window.addEventListener('resize', () => {
console.log(document.body.clientWidth, window.innerWidth)
})
</script>
</body>
</html>
打开控制台,移动边线,可以看到控制台打印的数据在变化:
注意,我在示例代码中加入了清除浏览器默认内外边距的样式:
* {
margin: 0;
padding: 0;
}
谷歌浏览器默认外边距为8px,所以
window.innerWidth
的值应该是等于document.body.clientWidth
+ 16px的
清除内外边距后,两者变相等了。
在适配不同设配的尺寸时,会用到这个属性。
scroll
鼠标滚动时触发
注意:ios视图中,滚动时不会触发scroll事件,只有当滚动结束后事件才会被触发。
剪贴板事件
cut
: 剪切时触发
copy
: 复制时触发
paste
: 粘贴时触发
copy
在该事件中,不能使用e.clipboardData.getData()
方法来获取剪切板数据,(cut
同理)
与e.preventDefault()
的关系:(cut
同理)
- 如果没有取消默认事件,
setData
方法失效,会把选中的内容放到剪切板 - 如果取消了默认事件,且调用了
setData
方法,就会把setData
中的数据放到剪切板,而非选中的内容。 - 如果取消了默认行为,但并没有调用
setData
方法,就没有任何行为,相当于没有任何操作。
paste
在该事件中,使用e.clipboardData.getData()
方法来获取剪切板数据
且必须使用e.preventDefault()
阻止掉默认行为。
如果不阻止默认行为,js脚本就无法接管用户的操作。
事件目标最好选用dom元素,而非document
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>hello world</p>
<textarea>test cut success</textarea>
<script>
// 绑定复制事件
document.addEventListener('copy', function(e) {
// 往剪贴板放文本数据
e.clipboardData.setData('text/plain', 'ok, you success ~')
// 阻止默认行为
e.preventDefault()
})
const textarea = document.querySelector('textarea')
// 给元素绑定粘贴事件, 有兼容性问题,所以这里最好绑定元素,而不是document
textarea.addEventListener('paste', function(e) {
// 获取粘贴板的数据
let paste = e.clipboardData.getData('text')
// 获取选中区域
const sel = window.getSelection()
// 页面初始化且未点击,rangeCount为0 ,
// 页面完成初始化,用户点击了页面的任意区域,rangeCount为1
if (!sel.rangeCount) return false
// 删除选中内容
sel.deleteFromDocument()
// 在光标处插入文本节点
sel.getRangeAt(0).insertNode(this.createTextNode(paste))
// 阻止默认行为
e.preventDefault()
})
</script>
</body>
</html>
推荐阅读: https://www.ruanyifeng.com/blog/2021/01/clipboard-api.html
键盘事件
keydown
: 按下任意按键
keypress
: 除 Shift、Fn、CapsLock 外的任意键被按住。(连续触发。)
keyup
: 抬起任意按键
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box"></div>
<script>
const box = document.querySelector('.box')
// 1. 按键的名称
document.addEventListener('keydown', function(e) {
console.log(e.code === 'ControlLeft' || e.code === 'ControlRight')
box.innerHTML = e.code
})
// 2. 按键的数字编码
document.addEventListener('keydown', function(e) {
box.innerHTML = e.keyCode
})
// 组合键
document.addEventListener('keydown', function(e) {
let ctrlKey = e.ctrlKey || e.metaKey // ctrl键
if(ctrlKey && e.keyCode === 67) {
e.preventDefault()
alert('你使用了Ctrl + C 组合键~')
}
})
</script>
</body>
</html>
鼠标事件
contextmenu
: 右击事件
示例:(尝试右击box区域,看下效果)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
background-color: pink;
border-radius: 4px;
}
.box1 {
position: absolute;
z-index: 1;
width: 200px;
height: 300px;
background-color: rgba(51 51 51 / 5%);
border: 1px solid rgba(51 51 51 / 15%);
border-radius: 6px;
display: none;
}
.box1 ul {
width: 100%;
margin: 0;
padding: 0;
}
.box1 ul li {
list-style-type: none;
width: 90%;
height: 40px;
margin: 6px auto;
text-align: center;
line-height: 40px;
border-radius: 6px;
cursor: pointer;
}
.box1 ul li:hover {
background-color: rgba(0 0 0 / 18%);
}
</style>
</head>
<body>
<div class="box"></div>
<div class="box1">
<ul>
<li>刷新</li>
<li>查看属性</li>
<li>审查</li>
<li>上一页</li>
<li>下一页</li>
<li>阻止默认</li>
</ul>
</div>
<script>
// 阻止右键的默认行为
/*document.addEventListener('contextmenu', e => {
e.preventDefault()
})*/
// 修改box区域内的右击菜单
const box = document.querySelector('.box')
const box1 = document.querySelector('.box1')
box.addEventListener('contextmenu', e => {
e.preventDefault()
box1.style.display = 'block'
box1.style.top = e.clientY + 'px'
box1.style.left = e.clientX + 'px'
})
</script>
</body>
</html>
另外,关于e.clientX,e.clientY e.pageX e.pageY e.offsetX e.offsetY
可以阅读: https://www.cnblogs.com/deerfig/p/6432683.html
也可以看图理解:
dblclick
: 双击事件
select
: 有文本被选中时触发
该属性仅对<input type="text" />
和 <textarea></textarea>
生效。
对别的元素不生效,对type不是text的input元素也不生效。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" value="hello world !">
<h3>你选中的文本内容是:</h3>
<script>
const inp = document.querySelector('input')
const h3 = document.querySelector('h3')
inp.addEventListener('select', e => {
// 截取选中的文本
let et = e.target
const sel = et.value.substring(et.selectionStart, et.selectionEnd)
h3.innerHTML = '你选中的文本内容是:' + sel
})
document.addEventListener('click', () => {
h3.innerHTML = '你选中的文本内容是:'
})
</script>
</body>
</html>
wheel
: 鼠标滚轮事件。存在兼容性问题。
拖放事件
drag
: 正在拖动元素或文本选区(在此过程中持续触发,每 350ms 触发一次)
当鼠标拖动到非可视区域后,拖放事件不会触发。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>hello world ~</p>
<script>
const p = document.querySelector('p')
p.addEventListener('drag', e => {
console.log('选中的元素被拖动了')
})
</script>
</body>
</html>
效果:
dragstart
: 开始拖动
dragend
: 拖放操作结束。(松开鼠标按钮或按下 Esc 键)
dragenter
: 被拖动的元素或文本选区移入有效释放目标区时触发
该事件有两种写法,
第一种是用document做监听对象
,然后对目标区域做判断
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
border-radius: 4px;
background-color: pink;
margin: 100px 200px;
}
</style>
</head>
<body>
<p>hello world ~</p>
<div class="box"></div>
<script>
const p = document.querySelector('p')
const box = document.querySelector('.box')
document.addEventListener('dragenter', e => {
console.log(e.target.className)
e.target.className === 'box' && console.log('进入box区域了')
})
</script>
</body>
</html>
第二种写法是用目标区域做监听
, 这时就不需要对目标区域做判断了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
border-radius: 4px;
background-color: pink;
margin: 100px 200px;
}
</style>
</head>
<body>
<p>hello world ~</p>
<div class="box"></div>
<script>
const p = document.querySelector('p')
const box = document.querySelector('.box')
box.addEventListener('dragenter', e => {
console.log('进入box区域了')
})
</script>
</body>
</html>
dragleave
: 被拖动的元素或文本选区移出有效释放目标区
写法也是两种,同dragenter
,
需要注意的是,被拖动的元素,一定是从目标区域离开时才会触发dragleave
dragover
: 被拖动的元素或文本选区正在有效释放目标上被拖动 (在此过程中持续触发,每350ms触发一次)
drop
: 元素在有效释放目标区上释放
这个事件过于特殊,
要想实现drop,一定要在dragover事件中添加阻止默认事件!!!
drop的监听对象也分两种写法,写法同drapenter
其实就是谁是监听对象,drop就可以在哪里触发。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
border-radius: 4px;
background-color: pink;
margin: 100px 200px;
}
</style>
</head>
<body>
<p>hello world ~</p>
<div class="box"></div>
<script>
const p = document.querySelector('p')
const box = document.querySelector('.box')
// 要想实现drop,一定要在dragover事件中添加阻止默认事件!!!
document.addEventListener('dragover', e => {
e.preventDefault()
})
box.addEventListener('drop', e => {
console.log('释放了')
})
</script>
</body>
</html>
媒体事件
视频/音频出于加载过程中时,大致会依次发生以下事件:
loadstart
: 当浏览器开始寻找指定的音频/视频时,会发生 loadstart 事件。即当加载过程开始时。durationchange
: 当指定音频/视频的时长数据发生变化时,发生 durationchange 事件。当音频/视频加载后,时长将由 “NaN” 变为音频/视频的实际时长。loadedmetadata
: 当指定的音频/视频的元数据已加载时,会发生 loadedmetadata 事件。音频/视频的元数据包括:时长、尺寸(仅视频)以及文本轨道。loadeddata
: 当当前帧的数据已加载,但没有足够的数据来播放指定音频/视频的下一帧时,会发生 loadeddata 事件。progress
: 当浏览器正在下载指定的音频/视频时,会发生 progress 事件。canplay
: 当浏览器能够开始播放指定的音频/视频时,发生 canplay 事件。canplaythrough
: 当浏览器预计能够在不停下来进行缓冲的情况下持续播放指定的音频/视频时,会发生 canplaythrough 事件。
// 示例
const video = document.querySolector('video')
video.addEventListener('loadstart', e => {
console.log('开始加载了~')
})
video属性以及事件总结,推荐阅读:
https://www.cnblogs.com/rogerwu/p/10072119.html
complete
: 当离线音频上下文的呈现完成时,将触发OfflineAudioContext
接口的事件
官方文档示例:
offlineAudioCtx.addEventListener('complete',()=> {
console.log('Offline audio processing now complete');
showModalDialog('Song processed and ready to play');
playBtn.disabled = false;
})
emptied
:
MDN原文解释:当一个视频变空时会触发。比如视频被加载或部分加载时,又唤起load方法去重新加载该视频。
W3C解释: 当发生不良情况且媒体文件突然不可用时,发生此事件
ended
: 在媒体播放到尽头时发生此事件
loadeddata
: 事件在第一帧数据加载完成后触发
loadedmetadata
: 加载元数据(比如尺寸和持续时间)时,发生此事件
pause
: 当媒体被用户暂停或以编程方式暂停时,发生此事件
play
: 当媒体已启动或不再暂停时,发生此事件。
playing
: 在媒体被暂停或停止以缓冲后播放时,发生此事件
ratechange
: 媒体播放速度改变时发生此事件。
seeked
: 当用户完成移动/跳到媒体中的新位置时,发生该事件
seeking
: 当用户开始移动/跳到媒体中的新位置时,发生该事件
stalled
: 当浏览器尝试获取媒体数据但数据不可用时,发生此事件。
suspend
: 当浏览器有意不获取媒体数据时,发生此事件
timeupdate
: 当currentTime更新时会触发
volumechange
: 当音量发生变化时触发
waiting
: 暂时缺乏数据而停止播放时触发,比如缓冲
进度事件
abort
当某个事情 比如媒体播放 、 网络请求等被中止但并非错误时触发
timeout
: 超时触发
Tab事件
visibilitychange
当其选项卡的内容变得可见或被隐藏时,会在文档上触发 visibilitychange
(能见度更改)事件。
该事件在页面初始加载时不会触发,即页面或tab第一次加载完成后,
并不会触发visibilitychange
事件,不会返回当前页面或tab的显示或隐藏状态。
只有在切换从当前页面或者tab栏切换到别的页面或者tab 、 或者从别的页面或tab切换回来后,
才会显示对应的状态。
注意:safari@14以下的版本不支持visibilitychange的window监听,所以在使用该事件时,
要使用document.addEventListener,而不是使用window.addEventListener
示例展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
document.addEventListener('visibilitychange', () => {
console.log('document.visibilityState: ', document.visibilityState)
})
</script>
</body>
</html>
打开控制台,刷新页面,可以看到,页面首次加载时,不会有状态打印:
从当前页面切换到别的页面时,当前页面的页面状态为hidden
:
从别的页面切换回当前页面时,当前页面的状态为visible
:
该事件作用有很多,比如在轮播图的定时器中,当页面切换出去后,
页面上的定时器还在计数,但是轮播图并没有在跑。
当切换回当前页面后,定时器会一下子跑很多下,造成轮播图的混乱。
用该事件监听页面的切换,当页面切换出去后,可以停掉定时器。
页面切换回来后,再开启定时器。这样就能很好的解决轮播图混乱问题。