事件
观察者模式
事件流描述了页面接收事件的顺序。
事件被定义为从最具体的元素(文档树中最深的节点)开始触发,然后向上传播到没有那么具体的元素(window)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div>
<div>
<div class="box">测试</div>
</div>
</div>
</body>
<script>
const box = document.querySelector(".box");
box.addEventListener("click", () => {
console.log("box");
});
document.addEventListener("click", () => {
console.log("document");
});
window.addEventListener("click", () => {
console.log("window");
});
</script>
</html>
可以看到,事件冒泡到了最上层window
事件捕获:从最外层触发事件,逐渐到最内层。 可以进行一些事件的拦截。
DOM0事件处理程序
把一个函数赋值给一个事件处理程序属性。
const box = document.querySelector(".box");
box.onclick = function () {
console.log("this==", this);
};
这里要注意,如果使用function字面量定义函数,则this是dom元素本身
如果用箭头函数,由于箭头函数本身没有this,this为window。
移除事件处理程序
const box = null
DOM事件处理程序
主要是两个方法: addEventListener() 和 removeEventListener
接收三个参数
- 事件名
- 事件处理函数
- options
-
- options
-
-
- capture: 捕获阶段传播到eventTarget触发
- once: 最多调用一次,调用完自动移除
- passive:永远不会调用preventDefault,如果调用了,会报错
- signal:
AbortSignal的abort方法被调用时,监听器被移除。
-
- useCapture: 控制捕获阶段处理还是冒泡阶段处理。
添加一个监听器
const box = document.querySelector(".box");
box.addEventListener("click", () => {
console.log("box");
});
移除监听器
方案1
兼容性不太好
// 为 table 添加可被移除的事件监听器
const controller = new AbortController();
const el = document.getElementById("outside");
el.addEventListener("click", modifyText, { signal: controller.signal });
// 改变 t2 内容的函数
function modifyText() {
const t2 = document.getElementById("t2");
if (t2.firstChild.nodeValue === "three") {
t2.firstChild.nodeValue = "two";
} else {
t2.firstChild.nodeValue = "three";
controller.abort(); // 当值变为 "three" 后,移除监听器
}
}
方案2
const box = document.querySelector(".box");
const handleClick = ()=>{}
box.addEventListener("click", handleClick);
box.removeEventListener("click",handleClick)
基于这个方法可以监听多个事件,多个事件处理程序按照添加顺序来触发。
事件对象
在dom发生事件时,所有相关信息都会被收集并存储在一个event对象中。
长这样、
事件类型
用户界面事件
涉及与BOM交互的通用浏览器事件
- DOMActivate: 元素被用户通过鼠标或键盘操作时激活,已经被废弃
- load: 在window上当页面加载完成后触发,在 上当所有窗格都加载完成后触发,在img元素上当图片加载完成后触发。
- unload:当页面完全卸载后触发
- abort: 被终止触发
- error:js报错触发,img无法加载图片触发。
- resize: 窗口变化触发
- scroll: 滚动触发
焦点事件(只写我觉得重要的把。。。 )
- blue: 失去焦点触发。不冒泡,所有浏览器支持。
- fouce: 获取焦点触发。不冒泡
- foucein:获得焦点触发,冒泡版本
- focusout:失去焦点触发,通用版
鼠标和滚轮事件
- click: 单机鼠标左键或按回车触发(无障碍)。
- dbclick:双击触发
- mousedown: 用户按下任意鼠标键时出发。
- mouseenter: 鼠标光标从元素外部移到元素内部时触发。这个事件不冒泡,不会再光标经过后代元素时触发。
- mouseleave: 用户把鼠标光标从元素内部移到元素外部触发。这个事件不冒泡,也不会在光标经过后代元素时触发。
- mousemove:鼠标光标在元素上移动时反复触发。
- mouseout: 从一个元素移动到另一个元素上触发。移动元素可以是原始元素的外部元素,也可以是原始元素的子元素。
- mouseover: 用户把鼠标光标从元素外部移到元素内部触发。
- mouseup: 用户释放鼠标键触发。
触发顺序
- mousedown
- mouseup
- click
- mousedown
- mouseup
- click
- dpclick
键盘与输入事件
- keydown: 用户按下键盘上某个键触发,持续按住会重复触发。
- keypress:用户按下键盘某个键并产生字符触发,持续按住会重复触发。 这个特性已经废弃,可以使用beforeinput
- keyup:用户释放键盘上某个键位触发。
HTML5事件
- contextmenu 事件: 上下文菜单概念,使用之前要先取消默认事件。
- beforeunload: 提供页面被卸载的机会。 会组织浏览器往返缓存。
- DOMContentLoaded :当 HTML 文档完全解析,且所有延迟脚本(
- readystatechange: 当文档的 readyState 属性发生改变时,会触发 readystatechange 事件。
DOMContentLoaded 不会等待样式表加载,但延迟脚本会等待样式表,而且 DOMContentLoaded 事件排在延迟脚本之后。此外,非延迟或异步的脚本(如
- pageshow:页面显示时触发。
- pagehide:页面隐藏时触发。
- hashchange:URL散列值发生变化时通知开发者。
设备事件
- orientationchange: orientationchange事件在设备的纵横方向改变时触发。
- deviceorientation : 事件在方向传感器输出新数据的时候触发。其数据系传感器与地球坐标系相比较所得,也就是说在设备上可能会采用设备地磁计的数据。
触摸及手势事件
- touchstart: 事件在一个或多个触点与触控设备表面接触时被触发。
- touchmove: 事件在触点于触控平面上移动时触发。
- touchend: 事件在一个或多个触点从触控平面上移开时触发。注意,也有可能触发 touchcancel 事件。
1. 事件触发逻辑上的区别
| 特性 | mouseenter | mouseover |
|---|---|---|
| 冒泡 | 不会冒泡 | 会冒泡 |
| 触发频率 | 只在鼠标首次进入目标元素时触发 | 鼠标进入目标元素及其子元素时都会触发 |
| 适合的场景 | 希望处理进入目标元素的事件(不包含子元素) | 需要监听鼠标进入目标和子元素的所有事件 |
示例 1: mouseenter 不会冒泡
javascript
复制代码
const parent = document.getElementById('parent');
const child = document.getElementById('child');
parent.addEventListener('mouseenter', () => {
console.log('mouseenter: Parent element');
});
child.addEventListener('mouseenter', () => {
console.log('mouseenter: Child element');
});
效果:
- 当鼠标进入
child时,只触发child的mouseenter事件,不会触发parent的mouseenter事件。 - 这是因为
mouseenter不会冒泡,即鼠标进入子元素时,不会触发父元素的事件。
示例 2: mouseover 会冒泡
javascript
复制代码
const parent = document.getElementById('parent');
const child = document.getElementById('child');
parent.addEventListener('mouseover', () => {
console.log('mouseover: Parent element');
});
child.addEventListener('mouseover', () => {
console.log('mouseover: Child element');
});
效果:
- 当鼠标进入
child时,会先触发child的mouseover事件,然后冒泡到parent,触发parent的mouseover事件。 - 这表明
mouseover事件会从子元素冒泡到父元素。
2. 触发频率的区别
mouseenter:只在鼠标首次进入目标元素时触发,即使鼠标在元素内的子元素之间移动,也不会再次触发。mouseover:当鼠标进入目标元素及其子元素时,都会触发多次。
示例 3: mouseenter vs mouseover 的触发频率
javascript
复制代码
const parent = document.getElementById('parent');
parent.addEventListener('mouseenter', () => {
console.log('mouseenter: Entered parent');
});
parent.addEventListener('mouseover', () => {
console.log('mouseover: Entered parent or child');
});
效果:
- 当鼠标进入
parent时,mouseenter和mouseover都会触发一次。 - 如果鼠标在
parent内的子元素之间移动:
-
mouseenter不会再触发。mouseover会在鼠标每次进入子元素时再次触发。
3. 总结:何时使用?
mouseenter:适用于希望在鼠标进入特定元素时触发事件,而忽略子元素的进入。
-
- 例子:在鼠标悬停于菜单项时高亮显示整个菜单。
mouseover:适用于需要监听鼠标进入元素及其子元素的所有事件。
-
- 例子:在鼠标悬停于某区域时,跟踪其路径或显示子元素的交互效果。
4. 图示帮助理解
mouseenter:
csharp
复制代码
[Parent Element] <- Mouse enters here: Triggered once
[Child Element] <- Mouse moves here: No new event triggered
2. mouseover:
arduino
复制代码
[Parent Element] <- Mouse enters: Triggered
[Child Element] <- Mouse enters: Triggered again (and bubbles to parent)
事件优化
事件委托
事件委托利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件。
删除事件处理程序
应该及时删除事件处理程序,否则会占用大量的内存。
模拟事件
最近应该用不上,后续再补充把~