事件基础

事件概述

事件是用户或浏览器执行的某种动作。

什么是事件

// 事件是用户与页面交互的方式
// - 点击按钮
// - 提交表单
// - 按下键盘
// - 鼠标移动
// - 页面加载完成

// 事件处理:当事件发生时执行的代码
const button = document.querySelector("button");
button.addEventListener("click", function() {
    console.log("按钮被点击");
});

事件三要素

// 1. 事件源:触发事件的元素
const button = document.querySelector("button");

// 2. 事件类型:什么类型的事件
const eventType = "click";

// 3. 事件处理程序:事件发生时执行的函数
function handler(event) {
    console.log("点击事件", event);
}

// 绑定事件
button.addEventListener(eventType, handler);

事件流

事件流描述了事件在DOM树中的传播顺序。

三个阶段

事件捕获阶段 → 目标阶段 → 事件冒泡阶段

document → html → body → div → body → html → document
         (捕获)      (目标)     (冒泡)

事件流图示

          捕获阶段              目标阶段              冒泡阶段
             ↓                    ↓                    ↑
┌─────────────────────────────────────────────────────────────┐
│                         document                            │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                         html                          │  │
│  │  ┌─────────────────────────────────────────────────┐  │  │
│  │  │                       body                      │  │  │
│  │  │  ┌───────────────────────────────────────────┐  │  │  │
│  │  │  │                    div                    │  │  │  │
│  │  │  │            (目标元素被点击)                │  │  │  │
│  │  │  └───────────────────────────────────────────┘  │  │  │
│  │  └─────────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

事件冒泡

事件从目标元素向上传播到document。

冒泡过程

<div id="grandparent">
    <div id="parent">
        <div id="child">点击我</div>
    </div>
</div>

<script>
const grandparent = document.querySelector("#grandparent");
const parent = document.querySelector("#parent");
const child = document.querySelector("#child");

grandparent.addEventListener("click", () => console.log("grandparent"));
parent.addEventListener("click", () => console.log("parent"));
child.addEventListener("click", () => console.log("child"));

// 点击child,输出顺序:
// child → parent → grandparent
</script>

冒泡的应用

// 事件委托:利用冒泡机制
const list = document.querySelector("ul");

list.addEventListener("click", function(e) {
    if (e.target.tagName === "LI") {
        console.log("点击了:", e.target.textContent);
    }
});

// 不需要给每个li绑定事件

事件捕获

事件从document向下传播到目标元素。

捕获过程

const grandparent = document.querySelector("#grandparent");
const parent = document.querySelector("#parent");
const child = document.querySelector("#child");

// 第三个参数为true,启用捕获
grandparent.addEventListener("click", () => console.log("grandparent"), true);
parent.addEventListener("click", () => console.log("parent"), true);
child.addEventListener("click", () => console.log("child"), true);

// 点击child,输出顺序:
// grandparent → parent → child

捕获与冒泡对比

const parent = document.querySelector("#parent");
const child = document.querySelector("#child");

parent.addEventListener("click", () => console.log("父-捕获"), true);
parent.addEventListener("click", () => console.log("父-冒泡"), false);
child.addEventListener("click", () => console.log("子"), false);

// 点击child,输出顺序:
// 父-捕获 → 子 → 父-冒泡

事件处理程序

绑定事件处理程序的方式。

HTML事件处理程序

<!-- 不推荐:HTML与JS混写 -->
<button onclick="console.log('点击')">按钮</button>
<button onclick="handleClick(event)">按钮</button>

<script>
function handleClick(e) {
    console.log("点击", e);
}
</script>

DOM0级事件处理程序

const button = document.querySelector("button");

// 赋值方式
button.onclick = function() {
    console.log("点击");
};

// 移除事件
button.onclick = null;

// 缺点:只能绑定一个处理程序

DOM2级事件处理程序

const button = document.querySelector("button");

// 添加事件监听器
button.addEventListener("click", function() {
    console.log("点击1");
});

button.addEventListener("click", function() {
    console.log("点击2");
});

// 可以绑定多个处理程序
// 点击时两个函数都会执行

// 移除事件监听器
function handler() {
    console.log("点击");
}

button.addEventListener("click", handler);
button.removeEventListener("click", handler);  // 需要同一个函数引用

事件处理程序对比

方式 多处理程序 移除 东巴文建议
HTML属性 不推荐
DOM0级 简单 简单场景
DOM2级 需引用 推荐

事件对象

事件对象包含事件的详细信息。

获取事件对象

const button = document.querySelector("button");

button.addEventListener("click", function(event) {
    // event就是事件对象
    console.log(event);
});

// 箭头函数
button.addEventListener("click", (event) => {
    console.log(event);
});

事件对象属性

button.addEventListener("click", function(e) {
    // 事件类型
    console.log(e.type);  // "click"
    
    // 事件目标
    console.log(e.target);       // 实际点击的元素
    console.log(e.currentTarget); // 绑定事件的元素
    
    // 事件阶段
    console.log(e.eventPhase);  // 1捕获 2目标 3冒泡
    
    // 时间戳
    console.log(e.timeStamp);
    
    // 是否可以冒泡
    console.log(e.bubbles);
    
    // 是否可以取消默认行为
    console.log(e.cancelable);
});

鼠标事件属性

element.addEventListener("click", function(e) {
    // 鼠标位置(相对于视口)
    console.log(e.clientX, e.clientY);
    
    // 鼠标位置(相对于页面)
    console.log(e.pageX, e.pageY);
    
    // 鼠标位置(相对于目标元素)
    console.log(e.offsetX, e.offsetY);
    
    // 鼠标位置(相对于屏幕)
    console.log(e.screenX, e.screenY);
    
    // 按键状态
    console.log(e.ctrlKey, e.shiftKey, e.altKey, e.metaKey);
    
    // 鼠标按钮
    console.log(e.button);  // 0左键 1中键 2右键
});

键盘事件属性

document.addEventListener("keydown", function(e) {
    // 按键值
    console.log(e.key);      // "Enter", "a", "A"
    console.log(e.code);     // "Enter", "KeyA"
    console.log(e.keyCode);  // 已废弃
    
    // 按键状态
    console.log(e.ctrlKey, e.shiftKey, e.altKey, e.metaKey);
    
    // 示例:检测快捷键
    if (e.ctrlKey && e.key === "s") {
        e.preventDefault();
        console.log("保存");
    }
});

this与事件对象

this的指向

const button = document.querySelector("button");

// 普通函数:this指向绑定事件的元素
button.addEventListener("click", function(e) {
    console.log(this === e.currentTarget);  // true
    console.log(this === button);           // true
});

// 箭头函数:this继承外层作用域
button.addEventListener("click", (e) => {
    console.log(this);  // 外层this(可能是window)
    console.log(e.currentTarget);  // button
});

使用建议

const button = document.querySelector("button");

// 需要this时用普通函数
button.addEventListener("click", function() {
    this.classList.add("clicked");
});

// 不需要this时可以用箭头函数
button.addEventListener("click", (e) => {
    e.target.classList.add("clicked");
});

// 或者使用event.currentTarget
button.addEventListener("click", (e) => {
    e.currentTarget.classList.add("clicked");
});

下一步

掌握了事件基础后,让我们继续学习:

  1. 事件绑定 - 学习事件绑定
  2. 事件类型 - 学习各种事件
  3. 事件高级 - 学习高级事件处理

东巴文(db-w.cn) - 让编程学习更简单

🎯 东巴文寄语:事件是JavaScript与用户交互的核心机制,理解事件流、事件冒泡和事件捕获,掌握事件对象的使用,是前端开发的基础。在 db-w.cn,我们帮你深入理解事件机制!