回顾
- 立即执行函数:马上执行
- 定时器函数:指定时间执行
- DOM 事件函数:特点条件触发并执行
# 一、DOM 事件
常用的事件使用方式:
var odiv = document.querySelector('.box')
odiv.onclick = function () {
console.log('你点击了div')
}
关于触发对象:
var odiv = document.querySelector('.box')
odiv.onclick = function (e) {
console.log(this === e.target) // true
}
# 1、DOM 0 级事件
- 单个 DOM0
box.onclick = function () {
console.log('点击成功')
}
// 根据对象的思想,下面的会覆盖上面的
box.onclick = function () {
console.log('你点击了div')
}
# 2、DOM 2 级事件
- 绑定多个 DOM2
box2.addEventListener('click', function () {
console.log('点击成功')
})
box2.addEventListener('click', function () {
console.log('你点击了div')
})
# 3、其他
- 事件解绑
// 1、普通方式dom0
btn.onclick = function () {
console.log('谢谢惠顾')
this.onclick = null
}
// 2、高级方式dom2(注意写法🎉)
function handler() {
console.log('谢谢惠顾')
this.removeEventListener('click', handler) // 一一对应
}
btn.addEventListener('click', handler)
# 二、事件类型
# 浏览器事件
在 BOM 部分我们知道了浏览器事件 onload
、onresize
、onscroll
等,在 DOM 事件中,我们可以结合它做一些事件类型内容小测试:
window.onload = function () {
var btn = document.getElementById('btn')
btn.onmousedown = function () {
// 方式1
this.style.background = '#ffefa1'
}
btn.onmouseup = function () {
// 方式2
btn.style.background = '#f4f9f9'
}
}
this 指向回顾
若使用的是事件绑定方法,那方法内的 this 指向绑定事件的对象
# 1、鼠标事件
- 点击(完整过程 --> 结果)
box.onclick = function () {
console.log('左键单击')
}
box.ondblclick = function () {
console.log('双击')
}
document.oncontextmenu = function () {
console.log('右键单击')
}
- 鼠标行为(拖拽功能)
mousedown //少
mouseup // 点击反悔👀(在其他地方松开)
mousemove // 移动时元素位置获取
- 鼠标状态(跟随功能)
// (子盒子有效)
mouseover ✨
mouseout
// (子盒子无效) 即:不会发生冒泡行为
mouseenter
mouseleave
mousemove // 移动时元素位置获取
应用:
1、自定义菜单
2、div 盒子鼠标拖动效果
# 2、键盘事件
通常在window
、document
、input
输入框等中使用。
<body>
<input type="text" id="username" />
<script>
username.onkeydown = function () {
console.log('按下了键盘')
// 后续可以判断按下的是什么键
}
username.onkeyup = function () {
console.log('抬起了键盘')
}
</script>
</body>
应用:
发表评论
# 3、表单事件
<body>
<form action="" id="myform">
<input type="text" id="username" />
<input type="reset" />
<input type="submit" value="提交" />
</form>
<script>
// 1、焦点角度
username.onfocus = function () {
console.log('获取焦点')
}
username.onblur = function () {
console.log('失去焦点')
// 内容校验
}
// 2、内容角度
username.oninput = function () {
console.log('输入内容时:内容发生改变')
}
username.onchange = function () {
console.log('失去焦点时:内容发生改变') // 💖
}
// 3、大局角度(form表单的事件)
myform.onreset = function () {
//
}
myform.onsubmit = function () {
// 内容校验
return false // 阻止提交跳转
}
</script>
</body>
应用:
表单校验
# 4、触摸事件(移动端)
touchstart // 手指触摸
touchmove // 滑动
touchend // 手指离开
# 三、应用场景
事件发生以后,会产生一个事件对象
,它会作为参数传给监听函数。
# 1、按键监听
keyCode 属性已经从 Web 标准中删除,这里了解即可。
username.onkeyup = function (event) {
// console.log(event)
// 兼容性处理
// event = event || window.event
if (event.keyCode === 13) {
console.log('你按下了enter键')
}
}
# 2、鼠标监听 ✨
鼠标监听事件中,event 对象中的有很多信息需要我们知道
box.onclick = function (event) {
// 1、位置
// 相对于target的坐标距离
console.log(event.offsetX, event.offsetY)
// 相对于浏览器的视口距离
console.log(event.clientX, event.clientY)
// 相对于电脑屏幕
console.log(event.screenX, event.screenX)
// 相对于Document的坐标距离
console.log(event.pageX, event.pageY)
// 倾斜角度(陀螺仪)
console.log(event.tiltX, event.tiltY)
// 2、其他
console.log(event.target)
}
图示(此处的 target 为 img 元素):
应用:
1、内容详细盒子的鼠标跟随效果
2、鼠标拖拽盒子效果
拓展:
.box {
pointer-events: none;
}
# 3、其他
略
# 四、DOM 事件流 🚩
# 1、事件的传播
典型的就是在包含父盒子、子盒子等嵌套的盒子上绑定事件时,会发现:
当我们触发子元素的事件时,若其外层元素也有 ✍ 同类型的事件,则也会自动触发。。
效果演示
观察:点击第一个 li 和 点击第二个 li 时控制台打印的内容有什么区别 —— 原生默认只有 🤔 绑定了第一个 li 元素(但是使用 jQuery 是不会出现这种情况的)。
<body>
<ul>
<li id="one">第一个子元素</li>
<li id="two">第二个子元素</li>
<li id="three">第三个子元素</li>
</ul>
<script>
document.querySelector('ul').onclick = function () {
console.log('父元素:', this)
}
// 默认值只为第一个li绑定click事件
document.querySelector('ul li').onclick = function () {
console.log(this.innerHTML)
}
// 绑定所有li
// 方式1:jQuery
// $('ul li').on('click', function () {
// console.log($(this).text())
// })
// 方式2:事件代理
// 略(后面解决)
</script>
</body>
总的来说,事件传播分为三个阶段:捕获、目标、冒泡。
注意
不是所有的事件都会经过三个阶段的传播过程,有些事件仅在目标元素上触发(并不会阻止事件传播机制的执行),常见的有:
load
、focus
、blur
、scroll
、resize
、reset
、submit
等
传播到最外层还有window
、document
(事件传播的 🤔 边界: 目标元素 和 document),下面看看我写的一个完整案例,体验一些事件传播:
注意
使用 onclick
或者 addEventListener
方法绑定事件处理程序时,默认情况下事件是在冒泡阶段进行处理。
如果需要在捕获阶段处理事件,可以使用 addEventListener 方法,并将第三个参数设置为 true。
完整案例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>事件传播示例</title>
<style>
#outer {
width: 300px;
height: 200px;
background-color: gray;
padding: 10px;
}
#inner {
width: 200px;
height: 100px;
background-color: white;
padding: 10px;
}
#button {
display: block;
margin-top: 20px;
padding: 5px 10px;
font-size: 16px;
background-color: blue;
color: white;
border: none;
}
</style>
</head>
<body>
<div id="outer">
outer盒子
<div id="inner">
inner盒子
<button id="button">点击我</button>
</div>
</div>
<script>
// 获取DOM元素
var outerDiv = document.getElementById('outer')
var innerDiv = document.getElementById('inner')
var buttonEl = document.getElementById('button')
// 注册事件监听器
outerDiv.addEventListener(
'click',
function (event) {
console.log('捕获阶段--outer盒子')
},
true
) // 捕获阶段(若设置为true,表示捕获阶段触发)
innerDiv.addEventListener(
'click',
function (event) {
console.log('捕获阶段--inner盒子')
},
true
)
// =================
buttonEl.addEventListener('click', function (event) {
console.log('按钮被点击了!')
event.stopPropagation() // 停止button元素的事件传播
})
// =================
innerDiv.addEventListener(
'click',
function (event) {
console.log('冒泡阶段--inner盒子')
},
false
) // 冒泡阶段(默认值为false,表示冒泡阶段触发)
outerDiv.addEventListener(
'click',
function (event) {
console.log('冒泡阶段--outer盒子')
console.log('------事件传播结束------')
},
false
)
</script>
</body>
</html>
# 2、阻止事件
有些情况下,我们可以对目标元素进行一些事件控制:
- 阻止事件冒泡(event.stopPropagation())
可以防止触发了一些不必要的事件
经典应用:弹窗
<div class="modal-box">
<div class="btn btn-primary sign-btn btn-lg show-modal " onclick="handleClick()">
立即报名
<div class="modal" id="myModal" tabindex="-1">
<div class="modal-dialog" role="document">
<div class="modal-content">
<button type="button" class="close" onclick="handleOk()">
<span aria-hidden="true">×</span>
</button>
<div class="modal-body">
<button class="subscribe" onclick="handleOk()">OK</button>
</div>
</div>
</div>
</div>
</div>
</div>
function handleClick() {
document.querySelector('#myModal').style.display = 'inline-block'
}
function handleOk() {
document.querySelector('#myModal').style.display = 'none'
window.event.stopPropagation()
}
注意
如果想要用 οnclick="xxx()" 来调用点击事件时,正确写法为:
function xxx() {
//阻止向上冒泡
window.event.stopPropagation()
}
buttonEl.addEventListener('click', function (event) {
// 以上面的综合案例为例
// 效果:冒泡阶段的事件就无法触发了
event.stopPropagation()
// ……
})
- 阻止默认行为(event.preventDefault())
myform.onsubmit = function () {
event.preventDefault()
// ……
return false // 阻止提交跳转
}
document.oncontextmenu = function () {
event.preventDefault()
// ……
// return false // 停止显示浏览器右键菜单栏
}
应用:
自定义右键菜单
# 3、事件委托/代理
JavaScript 中的事件代理是一种利用事件冒泡机制,将事件处理程序绑定到祖先元素(通常是父元素)上,以便处理其后代元素触发的事件。
当后代元素触发事件时,事件将冒泡到祖先元素,并且由该元素的事件处理程序进行处理。
- 父元素中的
event.target
体验一下在事件冒泡机制下,父元素的事件是如何触发的
<body>
<ul id="list">
<li>
<p>lencamo</p>
<button id="inner">点击一些看看效果</button>
</li>
</ul>
<script>
list.onclick = function (event) {
console.log('ul触发了click事件')
// 点击了自己/子元素
console.log('你点击了:', event.target)
// console.log(event.target, '触发了click事件')
}
</script>
</body>
效果演示-案例升级
通过将 click 事件处理程序绑定到 ul 元素上,来处理其后代 li 元素的点击事件
<body>
<ul>
<li id="one">第一个子元素</li>
<li id="two">第二个子元素</li>
<li id="three">第三个子元素</li>
</ul>
<script>
document.querySelector('ul').onclick = function () {
if (event.target.tagName === 'LI') {
console.log(event.target.innerText)
}
console.log('父元素:', this)
}
</script>
</body>
# 4、event 对象
对应事件对象 event,在 DOM 事件流中,我们要重点关注event.target
和 event.currentTarget
的区别
<div class="outer" onclick="outerClickFn()">
<div class="inner"></div>
</div>
function outerClickFn(event) {
console.log(window.event.target) // 触发事件的元素
// console.log(window.event.currentTarget) // 执行(处理)事件的元素
}