一文详解JavaScript的事件监听(最新整理)
作者:DustinWangq
一、认识事件处理
1.认识事件
Web页面需要经常和用户之间进行交互,而交互的过程中我们可能想要捕捉这个交互的过程:
- 比如
用户点击了某个按钮
、用户在输入框里面输入了某个文本
、用户鼠标经过了某个位置
; - 浏览器需要搭建一条
JavaScript代码和事件之间的桥梁
; - 当某个事件发生时,让JavaScript可以
响应
(执行某个函数),所以我们需要针对事件编写处理程序
(handler);
如何进行事件监听呢?
- 事件监听方式一:在
script
中直接监听(很少使用); - 事件监听方式二:DOM属性,通过元素的
on
来监听事件; - 事件监听方式三:通过EventTarget中的
addEventListener
来监听;
<body> <!-- 监听方式一 script--> <button onclick="alert('hello world');">nihao1</button> <button>nihao2</button> <button>nihao3</button> <script> // 监听方式二 on var btn2El = document.body.children[1]; btn2El.onclick = function () { alert("监听方式2"); } // 监听方式三 addListenEvent var btn3El = document.body.children[2]; btn3El.addEventListener("click", function () { alert("监听方式3"); }) </script>
2.常见的事件列表
鼠标事件:
- click —— 当鼠标点击一个元素时(触摸屏设备会在点击时生成)。
- mouseover / mouseout —— 当鼠标指针移入/离开一个元素时。
- mousedown / mouseup —— 当在元素上按下/释放鼠标按钮时。
- mousemove —— 当鼠标移动时。
键盘事件:
- keydown 和 keyup —— 当按下和松开一个按键时。
表单(form)元素事件:
- submit —— 当访问者提交了一个
<form>
时。 - focus —— 当访问者聚焦于一个元素时,例如聚焦于一个
<input>
。
Document 事件:
- DOMContentLoaded —— 当 HTML 的加载和处理均完成,DOM 被完全构建完成时。
CSS 事件:
- transitionend —— 当一个 CSS 动画完成时。
二、事件冒泡捕获
1.认识事件流
事实上对于事件有一个概念叫做事件流,为什么会产生事件流呢?
- 我们可以想到一个问题:当我们在浏览器上
对着一个元素点击时,你点击的不仅仅是这个元素本身
; - 这是因为我们的HTML元素是存在
父子元素叠加层级
的; - 比如一个span元素是放在div元素上的,div元素是放在body元素上的,body元素是放在html元素上的;
<div class="box"> <span class="word">哈哈哈哈</span> </div>
// 认识事件流 var boxEl = document.querySelector(".box"); var wordEl = document.querySelector(".word"); boxEl.addEventListener("click", function() { console.log("boxboxbox"); }) wordEl.addEventListener("click", function () { console.log("wordwordword"); })
2.事件冒泡和事件捕获
我们会发现默认情况下事件是从最内层的span向外依次传递的顺序,这个顺序我们称之为事件冒泡
(Event Bubble);
事实上,还有另外一种监听事件流的方式就是从外层到内层(body -> span),这种称之为事件捕获
(Event Capture);
为什么会产生两种不同的处理流呢?
- 这是因为早期浏览器开发时,不管是IE还是Netscape公司都发现了这个问题;
- 但是他们采用了
完全相反
的事件流来对事件进行了传递; - IE采用了事件冒泡的方式,Netscape采用了事件捕获的方式;
那么我们如何去监听事件捕获的过程呢?
3.事件捕获和冒泡的过程
如果我们都监听,那么会按照如下顺序来执行:
捕获阶段
(Capturing phase):
事件(从 Window)向下走近元素。
目标阶段
(Target phase):
事件到达目标元素。
冒泡阶段
(Bubbling phase):
事件从元素上开始冒泡。
事实上,我们可以通过event对象来获取当前的阶段:
- eventPhase
开发中通常会使用事件冒泡
,所以事件捕获了解即可。
// 默认情况下是事件冒泡 spanEl.addEventListener("click", function() { console.log("span元素发生了点击~冒泡") }) divEl.addEventListener("click", function() { console.log("div元素发生了点击~冒泡") }) bodyEl.addEventListener("click", function() { console.log("body元素发生了点击~冒泡") }) // 设置希望监听事件捕获的过程 spanEl.addEventListener("click", function() { console.log("span元素发生了点击~捕获") }, true) divEl.addEventListener("click", function() { console.log("div元素发生了点击~捕获") }, true) bodyEl.addEventListener("click", function() { console.log("body元素发生了点击~捕获") }, true)
三、事件对象event
1.事件对象
当一个事件发生时,就会有和这个事件相关的很多信息:
- 比如事件的
类型
是什么,你点击的是哪一个元素
,点击的位置
是哪里等等相关的信息; - 那么这些信息会被封装到一个
Event对象
中,这个对象由浏览器创建
,称之为event对象
; - 该对象给我们提供了想要的一些属性,以及可以通过该对象进行某些操作;
如何获取这个event对象呢?
- event对象会在传入的事件处理(event handler)
函数回调
时,被系统传入; - 我们可以在回调函数中拿到这个event对象;
这个对象中都有哪些常见的属性和操作呢?
2.event常见的属性和方法
常见的属性:
- type:事件的类型;
- target:当前事件发生的元素;
- currentTarget:当前处理事件的元素;
- eventPhase:事件所处的阶段;
- offsetX、offsetY:事件发生在元素内的位置;
- clientX、clientY:事件发生在客户端内的位置;
- pageX、pageY:事件发生在客户端相对于document的位置;
- screenX、screenY:事件发生相对于屏幕的位置;
常见的方法:
preventDefault
:取消事件的默认行为;stopPropagation
:阻止事件的进一步传递(冒泡或者捕获都可以阻止);
// 认识事件流 var boxEl = document.querySelector(".box"); var wordEl = document.querySelector(".word"); // 事件冒泡 wordEl.addEventListener("click", function (event) { console.log("word冒泡", event.target, event.currentTarget); console.log(event.type);//click console.log(event.eventPhase);//2 }) wordEl.addEventListener("click", function (event) { console.log("word捕获", event.target, event.currentTarget); console.log(event.type);//click console.log(event.eventPhase);//2 }, true) boxEl.addEventListener("click", function (event) { console.log("box捕获", event.target, event.currentTarget); console.log(event.type);//click console.log(event.eventPhase);//1 }, true) document.body.addEventListener("click", function (event) { console.log("body捕获", event.target, event.currentTarget); console.log(event.type);//click console.log(event.eventPhase);//1 // 停止传递 // event.stopPropagation(); }, true) var aEl = document.querySelector("a"); console.log(aEl); console.log(aEl.href); aEl.addEventListener("click", function (event) { console.log("a标签冒泡"); // 阻止默认行为:比如a标签的跳转 event.preventDefault(); })
3.事件处理中的this
在函数中,我们也可以通过this来获取当前的发生元素:
divEl.onclick = function(event) { console.log(this) console.log(event.currentTarget) console.log(divEl) console.log(this === divEl) }
这是因为在浏览器内部,调用event handler是绑定到当前的target上的
四、EventTarget使用
1.EventTarget类
我们会发现,所有的节点、元素都继承自EventTarget
- 事实上Window也继承自EventTarget;
那么这个EventTarget是什么呢?
- EventTarget是一个DOM接口,主要用于
添加
、删除
、派发
Event事件;
EventTarget常见的方法:
addEventListener
:注册某个事件类型以及事件处理函数;removeEventListener
:移除某个事件类型以及事件处理函数;dispatchEvent
:派发某个事件类型到EventTarget上;
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)
// 派发事件 setTimeout(() => { // 3秒后触发点击事件 btnEl.dispatchEvent(new Event("click")); }, 3000);
五、事件委托模式
1.事件委托
事件冒泡在某种情况下可以帮助我们实现强大的事件处理模式 – 事件委托模式
(也是一种设计模式)
那么这个模式是怎么样的呢?
- 因为当
子元素被点击
时,父元素可以通过冒泡可以监听到子元素的点击
; - 并且可以通过
event.target
获取到当前监听的元素;
案例:一个ul中存放多个li,点击某一个li会变成红色
- 方案一:监听每一个li的点击,并且做出相应;
- 方案二:在ul中监听点击,并且通过event.target拿到对应的li进行处理;
- 因为这种方案并不需要遍历后给每一个li上添加事件监听,所以它更加高效;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> ul,li { list-style: none; margin: 0; padding: 0; } .item { display: inline-block; padding: 20px; } .active { background-color: yellow; color: blue; } </style> </head> <body> <ul> <li class="item">吃饭睡觉打豆豆</li> <li class="item">吃饭睡觉打豆豆</li> <li class="item">吃饭睡觉打豆豆</li> <li class="item">吃饭睡觉打豆豆</li> <li class="item">吃饭睡觉打豆豆</li> </ul> <script> var ulEl = document.querySelector("ul"); ulEl.addEventListener("click", function (event) { // 委托模式 // 获取触发事件的元素 event.target.classList.toggle("active"); }) </script> </body> </html>
2.事件委托的标记
某些事件委托可能需要对具体的子组件进行区分,这个时候我们可以使用data-*
对其进行标记:
比如多个按钮的点击,区分点击了哪一个按钮:
<!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"> <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> </html>
六、常见的事件
1.常见的鼠标事件
接下来我们来看一下常见的鼠标事件(不仅仅是鼠标设备,也包括模拟鼠标的设备,比如手机、平板电脑)
常见的鼠标事件:
1.1 mouseover和mouseenter的区别
mouseenter和mouseleave
- 不支持冒泡
- 进入子元素依然属于在该元素内,没有任何反应
mouseover和mouseout
支持冒泡
- 进入元素的子元素时
- 先调用父元素的mouseout
- 再调用子元素的mouseover
- 因为支持冒泡,所以会将mouseover传递到父元素中;
var boxEl = document.querySelector(".box"); var containerEl = document.querySelector(".container"); // mouseenter mouseleave // containerEl.addEventListener("mouseenter", function (event) { // console.log("鼠标进来拉") // }) // containerEl.addEventListener("mouseleave", function (event) { // console.log("鼠标离开拉") // }) // mouseover mouseout containerEl.addEventListener("mouseover", function (event) { console.log("鼠标进来拉") }) containerEl.addEventListener("mouseout", function (event) { console.log("鼠标离开拉") })
2.常见的键盘事件
常见的键盘事件:
事件的执行顺序是 onkeydown、onkeypress、onkeyup
- down事件先发生;
- press发生在文本被输入;
- up发生在文本输入完成;
我们可以通过key和code来区分按下的键:
- code:“按键代码”(“KeyA”,“ArrowLeft” 等),特定于键盘上按键的物理位置。
- key:字符(“A”,“a” 等),对于非字符(non-character)的按键,通常具有与 code 相同的值。
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() } }
3.常见的表单事件
针对表单也有常见的事件:
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() }
4.文档加载事件
DOMContentLoaded
:浏览器已完全加载 HTML,并构建了 DOM 树,但像<img>
和样式表之类的外部资源可能尚未加载 完成。
load
:浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等。
// 注册事件监听 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) }) window.onload = function() { console.log("文档中所有资源都加载完毕") // var imgEl = document.querySelector("img") // console.log("图片的宽度和高度:", imgEl.offsetWidth, imgEl.offsetHeight) } window.onresize = function() { console.log("创建大小发生改变时") }
事件类型:https://developer.mozilla.org/zh-CN/docs/Web/Events
到此这篇关于JavaScript的事件监听的文章就介绍到这了,更多相关JavaScript的事件监听内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!