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 清理,避免内存泄漏。

相关推荐
宇晨T1 小时前
从零搭建企业级Web服务:Shell脚本 + LNMP + Docker 实战指南
前端·docker·容器
禅思院1 小时前
AI对话前端从入门到崩溃:一个长对话引发的五层优化战争【四】
前端·架构·前端框架
CodeStats1 小时前
【Linux IO】从文件描述符到硬件中断:Linux IO 系统底层完全拆解
java·linux·开发语言·io·socket
恋猫de小郭1 小时前
Flutter Windows 开始支持 Impeller ,还修复了多窗口 bug
android·前端·flutter
着迷不白1 小时前
Linux系统故障修复、日志管理与安全加固实战指南
开发语言·php
柳杉1 小时前
没写一行代码!我用 ChatGPT 5.6 (Sol) 做了一个智慧充电站 3D 大屏
前端·chatgpt·数据可视化
贺国亚1 小时前
A2A协议与Agent互操作-Task生命周期
开发语言·qt
Henrii_历小海2 小时前
WhatsApp Web 不是手机镜像:多设备协议、本地数据流与出海获客流水线
前端·智能手机
炸薯条!2 小时前
从零开始学C++(4) --类和对象
开发语言·c++·算法