JavaScript 事件介绍

文章目录

  • [1. 事件流:捕获与冒泡](#1. 事件流:捕获与冒泡)
  • [2. 绑定事件处理程序](#2. 绑定事件处理程序)
  • [3. 事件对象](#3. 事件对象)
  • [4. 事件委托(Event Delegation)](#4. 事件委托(Event Delegation))
  • [5. 常见事件类型速览](#5. 常见事件类型速览)
  • [6. 自定义事件](#6. 自定义事件)
  • [7. 几个重要技巧与注意事项](#7. 几个重要技巧与注意事项)

JavaScript 中的事件,可以理解成浏览器或用户发出的"信号"。当页面上的某个事情发生时(比如点击按钮、按下键盘、页面加载完成),浏览器就会产生一个对应的事件,我们可以通过编写代码来"监听"并响应这些信号,让网页变得可交互。

1. 事件流:捕获与冒泡

当一个元素上的事件被触发时,事件并不会只在该元素上处理,而是沿着 DOM 树传播。这个过程分为三个阶段:

1、捕获阶段:事件从最外层的 window 开始,逐层向下传递,直到到达触发事件的元素。

2、目标阶段:事件到达实际触发的那个元素。

3、冒泡阶段:事件从触发元素开始,逐层向上冒泡,直到 window。

默认情况下,事件处理程序会在冒泡阶段执行,但我们可以选择在捕获阶段就处理它。

bash 复制代码
<div id="outer">
  <div id="inner">点击这里</div>
</div>
bash 复制代码
const outer = document.getElementById('outer');
const inner = document.getElementById('inner');

// 冒泡阶段(默认)
outer.addEventListener('click', () => console.log('外层冒泡'), false);
inner.addEventListener('click', () => console.log('内层冒泡'), false);

// 捕获阶段
outer.addEventListener('click', () => console.log('外层捕获'), true);
inner.addEventListener('click', () => console.log('内层捕获'), true);

// 点击 inner 后输出顺序:
// 外层捕获 → 内层捕获 → 内层冒泡 → 外层冒泡

2. 绑定事件处理程序

有三种方式为元素绑定事件:

① HTML 行内属性

bash 复制代码
<button onclick="handleClick()">按钮</button>

缺点:将结构与行为耦合,不利于维护,且作用域链特殊,不推荐使用。

② DOM 属性赋值

bash 复制代码
const btn = document.querySelector('button');
btn.onclick = function() {
  console.log('点击');
};
// 取消绑定
btn.onclick = null;

缺点:同一事件只能绑定一个处理函数,后面的会覆盖前面的。

③ addEventListener (推荐)

bash 复制代码
btn.addEventListener('click', function(e) {
  console.log('第一次绑定');
});
btn.addEventListener('click', function(e) {
  console.log('第二次绑定');
});
// 两个都会执行,不会互相覆盖

可以绑定多个处理函数。

可通过第三个参数控制是在捕获阶段还是冒泡阶段执行:

true / { capture: true }:捕获阶段

false / { capture: false }:冒泡阶段(默认)

可以通过 removeEventListener 解绑,但必须传入同一个函数引用。

现代开发中,第三个参数更多使用选项对象:

bash 复制代码
btn.addEventListener('click', function handler() {console.log('只绑定一次,再捕获阶段触发');},{
  once:true,    // 只执行一次,自动解绑
  passive: true, // 不会调用 preventDefault(),提升滚动性能
  capture: false // 是否在捕获阶段触发
});

3. 事件对象

事件处理程序会收到一个自动生成的 event 对象,包含了与该事件相关的所有信息。

bash 复制代码
element.addEventListener('click', function(event) {
  // 常用属性
  event.type          // 事件类型,如 'click'
  event.target        // 实际触发事件的元素(最内层)
  event.currentTarget // 绑定事件监听器的元素(即 this)
  event.clientX       // 鼠标相对于视口的 X 坐标
  event.clientY       // 鼠标相对于视口的 Y 坐标
  event.key           // 键盘事件中按下的键值
  event.keyCode       // 已废弃,请使用 key

  // 常用方法
  event.preventDefault()   // 阻止默认行为(如链接跳转、表单提交)
  event.stopPropagation()  // 阻止事件继续冒泡或捕获
  event.stopImmediatePropagation() // 阻止冒泡,并阻止同一元素上的其他监听器执行
});

target 和 currentTarget 的区别很重

target:你真正点击的那个元素(源头)。

currentTarget:绑定了当前处理函数的那个元素(监听器所在)。

4. 事件委托(Event Delegation)

如果有很多子元素需要绑定同一个事件(比如列表项),可以利用冒泡机制,将监听器加在它们的共同父元素上,然后通过 event.target 判断具体是哪个子元素。

优点:

减少内存:只需一个监听器,而不是给每个子元素都绑定。

支持动态元素:后续通过 JS 添加的子元素也能自动拥有该行为。

bash 复制代码
const list = document.getElementById('list');

list.addEventListener('click', function(e) {
  // 判断点击的是否是 li
  if (e.target.tagName === 'LI') {
    console.log('你点击了:', e.target.textContent);
  }
});

使用 matches 方法更灵活:

bash 复制代码
list.addEventListener('click', function(e) {
  if (e.target.matches('.item-class')) {
    // 处理
  }
});

5. 常见事件类型速览

鼠标事件

click、dblclick

mousedown、mouseup

mousemove

mouseenter、mouseleave(不冒泡)

mouseover、mouseout(会冒泡)

contextmenu(右键菜单)

键盘事件

keydown、keyup(推荐用来检测按键)

keypress(已废弃,不推荐)

表单事件

submit:表单提交时触发,常用于验证。

change:输入框内容改变且失去焦点时,或下拉选项改变时触发。

input:输入框内容实时改变时触发(更适合实时监听)。

focus、blur:元素获得/失去焦点(不冒泡)。

focusin、focusout:获得/失去焦点(会冒泡,可用于委托)。

文档/窗口事件

DOMContentLoaded:HTML 解析完毕,无需等待样式表、图片加载完。

load:页面所有资源(含图片)加载完成。

resize:窗口大小改变。

scroll:滚动事件(可配合 passive 优化性能)。

beforeunload:页面即将卸载,可用来提示用户保存。

触摸事件(移动端)

touchstart、touchmove、touchend

其他

copy、cut、paste:剪贴板事件。

dragstart、dragover、drop 等:拖拽事件。

6. 自定义事件

除了浏览器原生事件,我们还可以自己创建并触发事件:

bash 复制代码
// 创建一个事件
const myEvent = new CustomEvent('myAction', {
  detail: { message: '自定义事件触发了' }
});

// 监听它
document.addEventListener('myAction', function(e) {
  console.log(e.detail.message);
});

// 触发
document.dispatchEvent(myEvent);

这在组件间通信或复杂交互中非常实用。

7. 几个重要技巧与注意事项

1、this 的指向:在通过 addEventListener 绑定的普通函数中,this 指向 event.currentTarget(即绑定事件的元素);如果使用箭头函数,this 会保持外层作用域的值。

2、性能优化:对于 scroll、resize、mousemove 等高频事件,应结合节流(throttle) 或防抖(debounce) 处理。

3、passive 选项:在滚动监听中设置 { passive: true } 可以告诉浏览器你不会调用 preventDefault(),从而让滚动更丝滑。

4、解绑事件:记得在适当的时机用 removeEventListener 清理,避免内存泄漏。

相关推荐
Wang's Blog几秒前
Java 项目实战: 外卖平台优化-前后端分离开发模式与工程拆分
java·开发语言
小呆呆66621 分钟前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
凤城老人36 分钟前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)40 分钟前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
sunshine22 girl1 小时前
Java学习六 Java常见API-2--String-3-字符串的截取替换和其他方法
java·开发语言·学习
LOVE️YOU1 小时前
Python 函数名、函数对象与“把函数作为参数传递”
开发语言·python
码艺-Alimjan1 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
sunshine22 girl2 小时前
Java学习六 Java常见API-1-Radom随机数
java·开发语言·学习
可乐鸡翅yeah_2 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒2 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端