1. 事件处理方案
-
事件监听方式一:在script中直接监听(很少使用)
-
事件监听方式二:DOM属性,通过元素的on来监听事件
-
事件监听方式三:通过EventTarget中的addEventListener来监听
<body> <!-- 直接在html中编写JavaScript代码(了解) --> <button onclick="console.log('按钮1发生了点击~');">按钮1</button> <button class="btn2">按钮2</button> <button class="btn3">按钮3</button> <script> // 1.获取元素对象 var btn2El = document.querySelector(".btn2") var btn3El = document.querySelector(".btn3") // 2.onclick属性 // function handleClick01() { // console.log("按钮2发生了点击~") // } // function handleClick02() { // console.log("按钮2的第二个处理函数") // } // 这种方式无法执行多个函数 // btn2El.onclick = handleClick01 // btn2El.onclick = handleClick02 // 3.addEventListener(推荐) btn3El.addEventListener("click", function() { console.log("第一个btn3的事件监听~") }) btn3El.addEventListener("click", function() { console.log("第二个btn3的事件监听~") }) btn3El.addEventListener("click", function() { console.log("第三个btn3的事件监听~") }) </script> </body>
2. 事件捕获冒泡
-
事件流:在浏览器上对着一个元素点击时,点击的不仅仅是这个元素本身,因为 HTML 元素是存在父子元素叠加层级的,比如一个span元素是放在div元素上的,div元素是放在body元素上的,body元素是放在html元素上的。
-
不同传递
- 默认情况下事件是从最内层的span向外依次传递的顺序,这个顺序称之为事件冒泡(Event Bubble)
- 另外一种监听事件流的方式就是从外层到内层(body -> span),这种称之为事件捕获(Event Capture)
- addEventListener("click", fn, true):第三个参数为 true 能够监听事件捕获
-
如果都监听,会按照如下顺序来执行:
- 捕获阶段(Capturing phase):事件(从 Window)向下走近元素
- 目标阶段(Target phase):事件到达目标元素
- 冒泡阶段(Bubbling phase):事件从元素上开始冒泡
3. 事件对象event
-
当一个事件发生时,会有和这个事件相关的很多信息:比如事件的类型,点击的是哪一个元素,点击的位置等等相关的信息,这些信息会被封装到一个Event对象中,这个对象由浏览器创建,称之为event对象
-
event对象会在传入的事件处理(event handler)函数回调时,被系统传入,可以在回调函数中拿到这个event对象
-
常见的属性:
- type:事件的类型
- target:当前事件发生的元素
- currentTarget:当前处理事件的元素
- eventPhase:事件所处的阶段
- offsetX、offsetY:事件发生在元素内的位置
- clientX、clientY:事件发生在客户端内的位置
- pageX、pageY:事件发生在客户端相对于document的位置
- screenX、screenY:事件发生相对于屏幕的位置
-
target和currentTarget的区别:
- 对于以下代码,如果点击的是div元素,两者完全一致,
- 如果点击的是span元素,target指的是span元素,而currentTarget指的是div元素(即当前绑定事件的元素,由于span冒泡监听到的)
<!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 { display: flex; width: 200px; height: 200px; background-color: orange; } span { width: 100px; height: 100px; background-color: #f00; } </style> </head> <body> <div class="box"> <span class="btn"> <button>按钮</button> </span> </div> <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br> <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br> <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br> <script> var divEl = document.querySelector("div") var btnEl = document.querySelector(".btn") divEl.onclick = function(event) { // 1.偶尔会使用 console.log("事件类型:", event.type) console.log("事件阶段:", event.eventPhase) // 2.使用较少 console.log("事件元素中位置", event.offsetX, event.offsetY) console.log("事件客户端中位置", event.clientX, event.clientY) console.log("事件页面中位置", event.pageX, event.pageY) console.log("事件在屏幕中位置", event.screenX, event.screenY) // 3.target/currentTarget console.log(event.target) console.log(event.currentTarget) console.log(event.currentTarget === event.target) } </script> </body> </html>
-
两个方法
- preventDefault:阻止默认行为
- stopPropagation:阻止事件传递
<!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 { display: flex; width: 200px; height: 200px; background-color: orange; } .box span { width: 100px; height: 100px; background-color: #f00; } </style> </head> <body> <a href="http://www.baidu.com">百度一下</a> <div class="box"> <span> <button>按钮</button> </span> </div> <script> // 1.阻止默认行为 // var aEl = document.querySelector("a") // aEl.onclick = function(event) { // console.log("a元素发生了点击~") // event.preventDefault() // } // 2.阻止事件进一步传递 var btnEl = document.querySelector("button") var spanEl = document.querySelector("span") var divEl = document.querySelector("div") divEl.addEventListener("click", function(event) { console.log("div的事件捕获监听~") // event.stopPropagation() }, true) spanEl.addEventListener("click", function() { console.log("span的事件捕获监听~") }, true) btnEl.addEventListener("click", function(event) { console.log("button的事件捕获监听~") // event.stopPropagation() }, true) divEl.addEventListener("click", function() { console.log("div的事件冒泡监听~") }) spanEl.addEventListener("click", function(event) { console.log("span的事件冒泡监听~") event.stopPropagation() }) btnEl.addEventListener("click", function() { console.log("button的事件冒泡监听~") }) </script> </body> </html>
4. 事件函数中的this
-
处理的元素
<body> <div> <button>按钮</button> </div> <script> var btnEl = document.querySelector("button") var divEl = document.querySelector("div") divEl.onclick = function(event) { console.log(this) console.log(event.currentTarget) console.log(divEl) console.log(this === divEl) } // divEl.addEventListener("click", function() { // console.log(this) // }) </script> </body>
5. EventTarget的使用
-
所有的节点、元素都继承自EventTarget
- Window也继承自EventTarget
-
EventTarget是一个DOM接口,主要用于添加、删除、派发Event事件
-
EventTarget常见的方法:
- addEventListener:注册某个事件类型以及事件处理函数
- removeEventListener:移除某个事件类型以及事件处理函数
- dispatchEvent:派发某个事件类型到EventTarget上
<body> <button>按钮</button> <script> var btnEl = document.querySelector("button") // 1.将监听函数移除的过程 // var foo = function() { // console.log("监听到按钮的点击") // } // btnEl.addEventListener("click", foo) // // 需求: 过5s钟后, 将这个事件监听移除掉 // setTimeout(function() { // btnEl.removeEventListener("click", foo) // }, 5000) // 这种做法是无法移除的 btnEl.addEventListener("click", function() { console.log("btn监听的处理函数~") }) setTimeout(function() { btnEl.removeEventListener("click", function() {}) }, 5000) // eventtarget就可以实现类似于事件总线的效果 window.addEventListener("abc", function() { console.log("监听到abc的呼唤~") }) setTimeout(function() { window.dispatchEvent(new Event("abc")) }, 5000) </script> </body>
6. 事件委托(delegation)
- 原理:利用事件的冒泡机制,以及事件对象中可以准确获知触发事件的元素机制(e.target),将子元素事件委托给父元素处理的现象
-
案例一:ul 中 li 点击时active,其他的 li 取消active
<!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> .active { color: red; font-size: 20px; background-color: orange; } </style> </head> <body> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> <li>10</li> </ul> <script> // 1.每一个li都监听自己的点击, 并且有自己的处理函数(自己的函数) // var liEls = document.querySelectorAll("li") // for (var liEl of liEls) { // // 监听点击 // liEl.onclick = function(event) { // event.currentTarget.classList.add("active") // } // } // 2.统一在ul中监听 // var ulEl = document.querySelector("ul") // ulEl.onclick = function(event) { // console.log("点击了某一个li", event.target) // event.target.classList.add("active") // } // 3.点击li变成active, 其他的取消active var ulEl = document.querySelector("ul") var activeLiEl = null ulEl.onclick = function(event) { // 1.将之前的active移除掉 // for (var i = 0; i < ulEl.children.length; i++) { // var liEl = ulEl.children[i] // if (liEl.classList.contains("active")) { // liEl.classList.remove("active") // } // } // 1.找到active的li, 移除掉active // var activeLiEl = ulEl.querySelector(".active") // if (activeLiEl) { // activeLiEl.classList.remove("active") // } // 1.变量记录的方式 // edge case if (activeLiEl && event.target !== ulEl) { activeLiEl.classList.remove("active") } // 2.给点击的元素添加active if (event.target !== ulEl) { event.target.classList.add("active") } // 3.记录最新的active对应的li activeLiEl = event.target } </script> </body> </html> -
案例二:多个按钮的区分
- data-*
<body> <div class="box"> <button data-action="search">搜索~</button> <button data-action="new">新建~</button> <button data-action="remove">移除~</button> <button>1111</button> </div> <script> var boxEl = document.querySelector(".box") boxEl.onclick = function(event) { var btnEl = event.target var action = btnEl.dataset.action switch (action) { case "remove": console.log("点击了移除按钮") break case "new": console.log("点击了新建按钮") break case "search": console.log("点击了搜索按钮") break default: console.log("点击了其他") } } </script> </body>
- data-*
7. 鼠标事件
7.1 常见的事件
<!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: orange;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
// 鼠标事件
var boxEl = document.querySelector(".box")
boxEl.onclick = function() {
console.log("click")
}
boxEl.oncontextmenu = function(event) {
console.log("点击了右键")
// 阻止默认行为
event.preventDefault()
}
// 变量记录鼠标是否是点下去的
var isDown = false
boxEl.onmousedown = function() {
console.log("鼠标按下去")
isDown = true
}
boxEl.onmouseup = function() {
console.log("鼠标抬起来")
isDown = false
}
boxEl.onmousemove = function() {
if (isDown) {
console.log("鼠标在div上面移动")
}
}
</script>
</body>
</html>
7.2 mouseenter和mouseover的区别
- mouseenter和mouseleave
- 不冒泡
- 进入子元素的时候, 不会有任何反应
- mouseover和mouseout
- 会冒泡
- 进入子元素
- 先离开out父元素
- 进入子元素over
- 并且会冒泡给父元素 over
<!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 { display: flex; justify-content: center; align-items: center; width: 200px; height: 200px; background-color: orange; } .box span { width: 100px; height: 100px; background-color: red; } </style> </head> <body> <div class="box"> <span></span> </div> <script> var boxEl = document.querySelector(".box") var spanEl = document.querySelector("span") // 1.第一组 boxEl.onmouseenter = function() { console.log("box onmouseenter") } boxEl.onmouseleave = function() { console.log("box onmouseleave") } spanEl.onmouseenter = function() { console.log("span onmouseenter") } spanEl.onmouseleave = function() { console.log("span onmouseleave") } // 第二组 // boxEl.onmouseover = function() { // console.log("box onmouseover") // } // boxEl.onmouseout = function() { // console.log("box onmouseout") // } </script> </body> </html>
8. 键盘事件
-
常见的键盘事件
-
事件的执行顺序是 onkeydown、onkeypress、onkeyup
- down事件先发生
- press发生在文本被输入
- up发生在文本输入完成(抬起、松开)
-
通过key和code来区分按下的键:
- code:“按键代码”("KeyA","ArrowLeft" 等),特定于键盘上按键的物理位置。
- key:字符 ("A","a" 等,大小写区分),对于非字符(non-character)的按键,通常具有与 code 相同的值。)
<body> <input type="text"> <button>搜索</button> <script> var inputEl = document.querySelector("input") var btnEl = document.querySelector("button") // inputEl.onkeydown = function() { // console.log("onkeydown") // } // inputEl.onkeypress = function() { // console.log("onkeypress") // } // inputEl.onkeyup = function(event) { // console.log(event.key, event.code) // } // 1.搜索功能 btnEl.onclick = function() { console.log("进行搜索功能", inputEl.value) } inputEl.onkeyup = function(event) { if (event.code === "Enter") { console.log("进行搜索功能", inputEl.value) } } // 2.按下s的时候, 搜索自动获取焦点 document.onkeyup = function(event) { if (event.code === "KeyS") { inputEl.focus() } } </script> </body> -
注意:keyCode 已废弃
9. 表单事件
<body>
<form action="/abc">
<input type="text">
<textarea name="" id="" cols="30" rows="10"></textarea>
<button type="reset">重置</button>
<button type="submit">提交</button>
</form>
<script>
var inputEl = document.querySelector("input")
// 1.获取焦点和失去焦点
// inputEl.onfocus = function() {
// console.log("input获取到了焦点")
// }
// inputEl.onblur = function() {
// console.log("input失去到了焦点")
// }
// 2.内容发生改变/输入内容
// 输入的过程: input
// 内容确定发生改变(离开): change
// inputEl.oninput = function() {
// console.log("input事件正在输入内容", inputEl.value)
// }
// inputEl.onchange = function() {
// console.log("change事件内容发生改变", inputEl.value)
// }
// 3.监听重置和提交
var formEl = document.querySelector("form")
formEl.onreset = function(event) {
console.log("发生了重置事件")
event.preventDefault()
}
formEl.onsubmit = function(event) {
console.log("发生了提交事件")
// axios库提交
event.preventDefault()
}
</script>
</body>
10. 文档加载
-
DOMContentLoaded:浏览器已完全加载 HTML,并构建了 DOM 树,但像
<img>和样式表之类的外部资源可能尚未加载完成。 -
load:浏览器不仅加载完成了 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; } </style> </head> <body> <script> // 注册事件监听 window.addEventListener("DOMContentLoaded", function() { // 1.这里可以操作box, box已经加载完毕 // var boxEl = document.querySelector(".box") // boxEl.style.backgroundColor = "orange" // console.log("HTML内容加载完毕") // 2.获取img对应的图片的宽度和高度 var imgEl = document.querySelector("img") console.log("图片的宽度和高度:", imgEl.offsetWidth, imgEl.offsetHeight) // 0 0 }) window.onload = function() { console.log("文档中所有资源都加载完毕") // var imgEl = document.querySelector("img") // console.log("图片的宽度和高度:", imgEl.offsetWidth, imgEl.offsetHeight) // 450 281 } // 屏幕大小发生变化 window.onresize = function() { console.log("创建大小发生改变时") } </script> <div class="box"> <p>哈哈哈啊</p> </div> <a href="#">百度一下</a> <img src="../images/kobe.jpg" alt=""> </body> </html> -
其他事件学习可参考MDN官网