一、事件的定义:
事件就是用户和页面之间发生的交互行为。
二、事件的作用:
可以给事件绑定响应函数(回调函数),来完成和用户之间的交互。
三、绑定响应函数的方式:
1、直接在元素属性中设置:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
</head>
<body>
<button id="btn" onclick="alert('点击按钮事件')">按钮</button>
</body>
</html>
2、通过元素的指定属性进行设置:
同样的事件,只能绑定一个回调函数。如果绑定了多个函数,后面的函数会覆盖掉前面的函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
</head>
<body>
<button id="btn">按钮</button>
<script>
let dom = document.getElementById("btn")
dom.onmouseenter = function() {
console.log('鼠标移入按钮事件')
}
</script>
</body>
</html>
3、通过元素的 addEventListener
方法:
同样的事件,可以绑定多个回调函数。执行顺序:先绑定先执行。
IE8
及以下不支持。this
指向绑定函数的对象。
关于参数:
参数1:事件的名称,类型是字符串
参数2:回调函数,事件触发的时候被调用
参数3:是否在捕获阶段触发,类型是布尔值,一般传false
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
</head>
<body>
<button id="btn">按钮</button>
<script>
let btnDom = document.getElementById("btn")
btnDom.addEventListener('dblclick', function(){
// this指向绑定函数的对象,在这个例子中,this就是btnDom
console.log('双击按钮事件',this)
})
</script>
</body>
</html>
4、使用 attachEvent()
:
同样的事件,可以绑定多个回调函数。执行顺序:先绑定后执行。
IE8
及以下支持。this
指向 window
关于参数:
参数1:事件的名称,加on,类型是字符串
参数2:回调函数,事件触发的时候被调用
四、文档加载:网页是自上而下加载的
如果网页内容未加载完毕,就去获取网页中未加载的内容对象,那么会使得对象获取失败。
解决办法:
1、将script
标签放在 body
的最后进行编写:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
</head>
<body>
<button id="btn">按钮</button>
<script>
// 将获取dom的操作放在body标签的最后面
let dom = document.getElementById("btn")
console.log(dom) // <button id="btn">按钮</button>
</script>
</body>
</html>
2、将 js
代码放在window.onload
的回调函数中:
回调函数在窗口中的内容加载完毕后再触发。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
<script>
let dom = document.getElementById("btn")
console.log(dom) // null
window.onload = function() {
let dom2 = document.getElementById("btn")
console.log(dom2) // <button id="btn">按钮</button>
}
</script>
</head>
<body>
<button id="btn">按钮</button>
</body>
</html>
3、将 js
代码放在 document
对象的 DOMContentLoaded
的回调函数中:
回调函数在当前文档中的内容加载完毕后再触发(执行时机相比window.onload
更早)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
<script>
let dom = document.getElementById("btn")
console.log(dom) // null
document.addEventListener('DOMContentLoaded', function(){
let dom2 = document.getElementById("btn")
console.log(dom2) // <button id="btn">按钮</button>
})
</script>
</head>
<body>
<button id="btn">按钮</button>
</body>
</html>
4、将 js
代码放在外部文件中,使用 defer
属性设置:
执行时机相比document
对象的 DOMContentLoaded
的回调函数更早。
五、取消事件的默认行为:
1、return false
:
只适用于 通过元素的指定属性进行绑定回调函数
的事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
<style>
html {
font-size: 20px;
}
</style>
</head>
<body>
<form action="">
<button type="button" id="button">直接设置标签属性 来 取消button的默认跳转行为</button>
</form>
<br><br>
<a href="javascript:;">直接设置标签属性 来 取消超链接的默认跳转行为</a>
<br><br>
<a href="#" id="hyperlink">通过 return false 来 取消超链接的默认跳转行为</a>
<script>
// let btnDom = document.getElementById("button")
// btnDom.onclick = function() {
// }
let aDom = document.getElementById("hyperlink")
aDom.onclick = function() {
return false
}
</script>
</body>
</html>
2、event.preventDefault()
:推荐
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
</head>
<body>
<a href="https://www.baidu.com/" id="link">点击去百度</a>
<script>
let boxDom = document.getElementById("link")
boxDom.addEventListener('click', function(event){
event.preventDefault()
console.log('取消点击事件默认行为')
})
</script>
</body>
</html>
六、事件对象:
https://developer.mozilla.org/en-US/docs/Web/API/Event
1、定义:
浏览器在事件触发时创建的对象,该对象中封装了事件相关的各种信息。
2、作用:
通过事件对象可以获取到事件的详细信息(例如:鼠标的实时坐标、键盘的按键等)。
3、如何获取:
浏览器在创建事件对象后,会将 事件对象
作为响应函数的参数传递。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
<style>
#box {
width: 300px;
height: 200px;
background-color: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div id="box">盒子</div>
<script>
// 鼠标移入,获取鼠标对象
let boxDom = document.getElementById("box")
// 方式一:
boxDom.onmousemove = function(event) {
console.log(event) // MouseEvent {isTrusted: true, screenX: 73, screenY: 260, clientX: 73, clientY: 139, …}
}
// 方式二:
boxDom.addEventListener('mousemove',function(event){
console.log(event) // MouseEvent {isTrusted: true, screenX: 73, screenY: 260, clientX: 73, clientY: 139, …}
})
</script>
</body>
</html>
4、事件对象的原型:
事件对象有其共同的祖先:Event
七、绑定事件的对象 和 触发事件的对象:
绑定事件的对象this
/ event.currentTarget
触发事件的对象event.target
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
<style>
#box {
background: pink;
}
</style>
</head>
<body>
<div id="box">
<button id="btn">按钮</button>
</div>
<script>
// 点击子元素时:先执行 子元素 事件,后执行 父元素 事件。
// 触发事件的对象--子 event.target = <button id="btn2">按钮2</button>
// 触发事件的对象--子 this = <button id="btn2">按钮2</button>
// 触发事件的对象--父 event.target = <button id="btn2">按钮2</button>
// 绑定事件的对象--父 this = <div id="box">...</div>
let boxDom = document.getElementById("box")
boxDom.addEventListener('click', function(event){
console.log('触发事件的对象--父', event.target)
console.log('绑定事件的对象--父',this, event.currentTarget)
})
// let boxBtnDom = document.getElementById("btn")
// boxBtnDom.addEventListener('click', function(event){
// // event.cancelBubble = true // 取消子元素冒泡,即点击子元素的时候,只执行子元素中的事件
// console.log('触发事件的对象--子', event.target)
// console.log('绑定事件的对象--子',this)
// })
</script>
</body>
</html>
八、事件冒泡(bubble
):
1、定义:
事件的向上传导:当后代元素的事件被触发时,其祖先元素的相同事件也会被触发。
事件的冒泡和元素的样式的无关,只和结构有关
2、使用场景:
简化代码( 事件委托):当子元素也需要绑定和父元素相同的事件时,当多个子元素需要绑定相同的事件时,可以直接将事件绑定在父元素上。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小蓝球跟随鼠标移动</title>
<style>
.box1 {
width: 400px;
height: 400px;
background: pink;
text-align: center;
line-height: 400px;
}
.box2 {
position: absolute;
width: 100px;
height: 100px;
background: skyblue;
border-radius: 50%;
text-align: center;
line-height: 100px;
}
</style>
</head>
<body>
<div class="box1">box1</div>
<div class="box2">box2</div>
<script>
let box1Dom = document.getElementsByClassName('box1')[0]
box1Dom.addEventListener('mousemove', function(event){
// 为box1取消事件冒泡,则box2的小球会被挡在box1外面
event.stopPropagation()
})
// 使小蓝球跟随鼠标移动
let box2Dom = document.getElementsByClassName('box2')[0]
document.addEventListener('mousemove', function(event){
box2Dom.style.left = event.x + 'px'
box2Dom.style.top = event.y + 'px'
})
</script>
</body>
</html>
3、阻止事件冒泡:
① event.cancelBubble = true
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
<style>
#box {
background: pink;
}
</style>
</head>
<body>
<div id="box">
<button id="btn">按钮</button>
</div>
<script>
// 点击子元素时:先执行 子元素 事件,后执行 父元素 事件。
// 触发事件的对象--子 event.target = <button id="btn2">按钮2</button>
// 触发事件的对象--子 this = <button id="btn2">按钮2</button>
// 触发事件的对象--父 event.target = <button id="btn2">按钮2</button>
// 绑定事件的对象--父 this = <div id="box">...</div>
let boxDom = document.getElementById("box")
boxDom.addEventListener('click', function(event){
console.log('触发事件的对象--父', event.target)
console.log('绑定事件的对象--父',this)
})
let boxBtnDom = document.getElementById("btn")
boxBtnDom.addEventListener('click', function(event){
event.cancelBubble = true // 取消子元素冒泡,即点击子元素的时候,只执行子元素中的事件
console.log('触发事件的对象--子', event.target)
console.log('绑定事件的对象--子',this)
})
</script>
</body>
</html>
② 取消事件的传导:event.stopPropagation()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
<style>
#box {
background: pink;
}
</style>
</head>
<body>
<div id="box">
<button id="btn">按钮</button>
</div>
<script>
// 点击子元素时:先执行 子元素 事件,后执行 父元素 事件。
// 触发事件的对象--子 event.target = <button id="btn2">按钮2</button>
// 触发事件的对象--子 this = <button id="btn2">按钮2</button>
// 触发事件的对象--父 event.target = <button id="btn2">按钮2</button>
// 绑定事件的对象--父 this = <div id="box">...</div>
let boxDom = document.getElementById("box")
boxDom.addEventListener('click', function(event){
console.log('触发事件的对象--父', event.target)
console.log('绑定事件的对象--父',this)
})
let boxBtnDom = document.getElementById("btn")
boxBtnDom.addEventListener('click', function(event){
event.stopPropagation() // 取消子元素事件的传导,即点击子元素的时候,只执行子元素中的事件
console.log('触发事件的对象--子', event.target)
console.log('绑定事件的对象--子',this)
})
</script>
</body>
</html>
九、事件委派:
1、定义:
将事件统一绑定到共同的父元素上,当后代元素的事件被触发时,会一直冒泡到祖先元素,从而通过祖先元素的相应函数来处理事件。
2、说明:
利用了事件冒泡的原理,减少事件的绑定次数,提高程序的性能。
3、细节处理:
如果只需要对个别元素进行事件的触发,那么需要使用 event.target
对当前点击的对象进行判断。
4、例子:为所有的列表添加点击事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM</title>
</head>
<body>
<button id="btn">点击按钮添加超链接</button>
<ul>
<li><a href="https://www.baidu.com/">链接1</a></li>
<li><a href="https://www.baidu.com/">链接2</a></li>
<li><a href="https://www.baidu.com/">链接3</a></li>
</ul>
<script>
let btnDom = document.getElementById("btn")
btnDom.addEventListener('click', function(event){
let ulDom = document.getElementsByTagName('ul')[0]
ulDom.insertAdjacentHTML('beforeend',`<li><a href="https://www.baidu.com/">新链接</a></li>`)
})
document.addEventListener('click',function(event){
let links = document.querySelectorAll('a')
if (Array.from(links).includes(event.target)) {
event.preventDefault();
console.log('点击超链接',event)
}
})
</script>
</body>
</html>
十、事件捕获:
1、定义:
事件由外向内的传导:元素的事件被触发时,会先去寻找当前元素的祖先元素中是否有该事件。
2、例子:
1)点击
box2元素
时,会先从box2 元素
开始向外寻找,一直找到document
。
默认 捕获阶段 不会触发事件。
2)然后从document
开始向内寻找box2元素
3)找到box2元素
后,会向外进行事件的触发。
结果:
<!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>事件捕获</title>
<style>
body {
margin: 0;
padding: 0;
}
#box1 {
position: relative;
width: 300px;
height: 300px;
background-color: rgb(241, 108, 108);
}
#box1::after {
content: 'box1';
display: inline-block;
position: absolute;
right: 0;
bottom: 0;
}
#box2 {
position: relative;
width: 200px;
height: 200px;
background-color: rgb(226, 226, 71);
}
#box2::after {
content: 'box2';
display: inline-block;
position: absolute;
right: 0;
bottom: 0;
}
#box3 {
position: relative;
width: 100px;
height: 100px;
background-color: rgb(192, 221, 255);
}
</style>
</head>
<body>
<div id="box1">
<div id="box2">
<div id="box3">box3</div>
</div>
</div>
<script>
let box1 = document.getElementById("box1")
let box2 = document.getElementById("box2")
let box3 = document.getElementById("box3")
box1.addEventListener('click',function(event){
console.log('我是box1')
})
box2.addEventListener('click',function(event){
console.log('我是box2')
})
box3.addEventListener('click',function(event){
console.log('我是box3')
})
</script>
</body>
</html>
3、捕获阶段就触发事件:
将
addEventListener()
的第三个参数设置为true
<!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>事件捕获</title>
<style>
body {
margin: 0;
padding: 0;
}
#box1 {
position: relative;
width: 300px;
height: 300px;
background-color: rgb(241, 108, 108);
}
#box1::after {
content: 'box1';
display: inline-block;
position: absolute;
right: 0;
bottom: 0;
}
#box2 {
position: relative;
width: 200px;
height: 200px;
background-color: rgb(226, 226, 71);
}
#box2::after {
content: 'box2';
display: inline-block;
position: absolute;
right: 0;
bottom: 0;
}
#box3 {
position: relative;
width: 100px;
height: 100px;
background-color: rgb(192, 221, 255);
}
</style>
</head>
<body>
<div id="box1">
<div id="box2">
<div id="box3">box3</div>
</div>
</div>
<script>
let box1 = document.getElementById("box1")
let box2 = document.getElementById("box2")
let box3 = document.getElementById("box3")
box1.addEventListener('click',function(event){
console.log('我是box1')
}, true)
box2.addEventListener('click',function(event){
console.log('我是box2')
}, true)
box3.addEventListener('click',function(event){
console.log('我是box3')
}, true)
</script>
</body>
</html>
结果:
4、阻止捕获:event.stopPropagation()
例子:点击
box3元素
,仅触发box1事件
<!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>事件捕获</title>
<style>
body {
margin: 0;
padding: 0;
}
#box1 {
position: relative;
width: 300px;
height: 300px;
background-color: rgb(241, 108, 108);
}
#box1::after {
content: 'box1';
display: inline-block;
position: absolute;
right: 0;
bottom: 0;
}
#box2 {
position: relative;
width: 200px;
height: 200px;
background-color: rgb(226, 226, 71);
}
#box2::after {
content: 'box2';
display: inline-block;
position: absolute;
right: 0;
bottom: 0;
}
#box3 {
position: relative;
width: 100px;
height: 100px;
background-color: rgb(192, 221, 255);
}
</style>
</head>
<body>
<div id="box1">
<div id="box2">
<div id="box3">box3</div>
</div>
</div>
<script>
let box1 = document.getElementById("box1")
let box2 = document.getElementById("box2")
let box3 = document.getElementById("box3")
box1.addEventListener('click',function(event){
event.stopPropagation(); // 从box1开始就不向内走了
console.log('我是box1')
}, true)
box2.addEventListener('click',function(event){
console.log('我是box2')
}, true)
box3.addEventListener('click',function(event){
console.log('我是box3')
}, true)
</script>
</body>
</html>
5、获取当前触发的事件处在什么阶段:event.eventPhase
得到的值有三种:1表示捕获阶段
、2表示目标阶段
、3表示冒泡阶段
例子:点击
box3元素
,查看当前事件处在什么阶段
<!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>事件捕获</title>
<style>
body {
margin: 0;
padding: 0;
}
#box1 {
position: relative;
width: 300px;
height: 300px;
background-color: rgb(241, 108, 108);
}
#box1::after {
content: 'box1';
display: inline-block;
position: absolute;
right: 0;
bottom: 0;
}
#box2 {
position: relative;
width: 200px;
height: 200px;
background-color: rgb(226, 226, 71);
}
#box2::after {
content: 'box2';
display: inline-block;
position: absolute;
right: 0;
bottom: 0;
}
#box3 {
position: relative;
width: 100px;
height: 100px;
background-color: rgb(192, 221, 255);
}
</style>
</head>
<body>
<div id="box1">
<div id="box2">
<div id="box3">box3</div>
</div>
</div>
<script>
let box1 = document.getElementById("box1")
let box2 = document.getElementById("box2")
let box3 = document.getElementById("box3")
box1.addEventListener('click',function(event){
console.log('我是box1', event.eventPhase) // 3
})
box2.addEventListener('click',function(event){
console.log('我是box2', event.eventPhase) // 3
})
box3.addEventListener('click',function(event){
console.log('我是box3', event.eventPhase) // 2
})
</script>
</body>
</html>
结果:
十一、事件的传播:
1、关于事件的传播,网景公司和微软公司有不同的观点:
① 微软公司:
事件应该是
由内向外
传播。当事件触发的时候,应该先触发当前元素的事件,然后再向当前元素的祖先元素上传播,即事件应该在冒泡阶段执行。
② 网景公司:
事件应该是
由外向内
传播。当事件触发的时候,应该先触发当前元素的最外层的祖先元素事件,然后向内传播给后代元素。
2、 W3C
结合了两个公司的方案,将事件传播分为了三个阶段:
① 捕获阶段
:
从 最外层的祖先元素 向 目标元素 进行事件的捕获。
② 目标阶段
:触发事件的对象
事件捕获到目标元素,开始在目标元素上触发事件。
③ 冒泡阶段
:
事件从 目标元素 向 其祖先元素 进行传递,依次触发祖先元素上的事件。
3、IE8
及以下浏览器没有捕获阶段。
十二、 键盘事件:
键盘事件一般绑定给 获取焦点的对象
或者 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>拖拽box1</title>
<style>
body {
margin: 0;
padding: 0;
}
#box1 {
position: relative;
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<div id="box1">box1</div>
<script>
window.onload = function() {
var box1Dom = document.getElementById("box1")
// 1、监听box1 鼠标按下 事件
box1Dom.onmousedown = function(e) {
e = e || window.event
// 4、优化鼠标的位置:鼠标对于元素的相对位置不变
// 获取鼠标相对于元素的位置
var ol = e.clientX - this.offsetLeft
var ot = e.clientY - this.offsetTop
console.log(ol,ot)
// 2、监听 鼠标的移动事件,并且将box1的位置进行更改
// 一定要监听全局的鼠标移动事件,如果绑定给box1,那么会出现只能向下走,不能向上走的情况
document.onmousemove = function(event) {
event = event || window.event
var newLeft = event.clientX - ol;
var newTop = event.clientY - ot;
// 防止元素移出视口左侧和右侧
newLeft = Math.max(0, Math.min(newLeft, window.innerWidth - box1Dom.offsetWidth));
// 防止元素移出视口顶部和底部
newTop = Math.max(0, Math.min(newTop, window.innerHeight - box1Dom.offsetHeight));
box1Dom.style.transform = `translate(${newLeft}px, ${newTop}px)`;
}
// 3、鼠标松开,将元素固定在当前位置
// 一定要监听全局的鼠标移动事件,如果绑定给box1,那么当页面中有其他模块的时候,当鼠标移动到其他模块之上然后松手,当前的box1还会移动。
document.onmouseup = function() {
// 取消鼠标移动事件
document.onmousemove = null
// 取消鼠标松开事件
document.onmouseup = null
// 取消ie的默认事件
box1Dom.releaseCapture?.()
}
this.setCapture?.()
// 当我们拖拽一个网页的时候,浏览器会默认去搜索当前内容,此时会导致拖拽功能异常,这个是浏览器的默认行为。
// 6、优化拖拽:取消默认行为
return false
}
}
</script>
</body>
</html>