Skip to content

事件处理

事件处理是 JavaScript 中实现用户交互的核心机制。通过事件处理,网页可以响应用户的操作,如点击、键盘输入、鼠标移动等。

什么是事件?

事件是用户或浏览器执行的某种动作,如点击按钮、页面加载完成、提交表单等。JavaScript 可以监听这些事件并在事件发生时执行相应的代码。

常见事件类型

鼠标事件

javascript
// click - 鼠标点击
element.addEventListener("click", function() {
    console.log("元素被点击了");
});

// dblclick - 鼠标双击
element.addEventListener("dblclick", function() {
    console.log("元素被双击了");
});

// mouseover - 鼠标悬停
element.addEventListener("mouseover", function() {
    console.log("鼠标悬停在元素上");
});

// mouseout - 鼠标离开
element.addEventListener("mouseout", function() {
    console.log("鼠标离开了元素");
});

// mousedown - 鼠标按下
element.addEventListener("mousedown", function() {
    console.log("鼠标在元素上按下");
});

// mouseup - 鼠标释放
element.addEventListener("mouseup", function() {
    console.log("鼠标在元素上释放");
});

键盘事件

javascript
// keydown - 键盘按下
inputElement.addEventListener("keydown", function(event) {
    console.log("按键被按下:", event.key);
});

// keyup - 键盘释放
inputElement.addEventListener("keyup", function(event) {
    console.log("按键被释放:", event.key);
});

// keypress - 字符键按下(已废弃,使用 input 事件代替)
inputElement.addEventListener("keypress", function(event) {
    console.log("字符键被按下:", event.key);
});

表单事件

javascript
// submit - 表单提交
formElement.addEventListener("submit", function(event) {
    event.preventDefault(); // 阻止默认提交行为
    console.log("表单被提交");
});

// change - 表单元素值改变
inputElement.addEventListener("change", function() {
    console.log("输入值改变了");
});

// input - 输入时触发
inputElement.addEventListener("input", function() {
    console.log("正在输入:", this.value);
});

// focus - 元素获得焦点
inputElement.addEventListener("focus", function() {
    console.log("输入框获得焦点");
});

// blur - 元素失去焦点
inputElement.addEventListener("blur", function() {
    console.log("输入框失去焦点");
});

窗口事件

javascript
// load - 页面加载完成
window.addEventListener("load", function() {
    console.log("页面完全加载");
});

// DOMContentLoaded - DOM 构建完成
document.addEventListener("DOMContentLoaded", function() {
    console.log("DOM 构建完成");
});

// resize - 窗口大小改变
window.addEventListener("resize", function() {
    console.log("窗口大小改变为:", window.innerWidth, "x", window.innerHeight);
});

// scroll - 页面滚动
window.addEventListener("scroll", function() {
    console.log("页面滚动到:", window.scrollY);
});

事件处理方式

HTML 内联事件处理器(不推荐)

html
<button onclick="alert('Hello')">点击我</button>

DOM 属性事件处理器

javascript
let button = document.querySelector("button");
button.onclick = function() {
    alert("Hello");
};

// 只能绑定一个处理函数
button.onclick = function() {
    console.log("这会覆盖之前的处理函数");
};

addEventListener 方法(推荐)

javascript
let button = document.querySelector("button");

// 添加事件监听器
button.addEventListener("click", function() {
    alert("Hello");
});

// 可以添加多个事件监听器
button.addEventListener("click", function() {
    console.log("按钮被点击了");
});

// 使用箭头函数
button.addEventListener("click", () => {
    console.log("箭头函数处理点击事件");
});

事件对象

事件处理函数会接收一个事件对象参数,包含有关事件的详细信息:

javascript
button.addEventListener("click", function(event) {
    console.log("事件类型:", event.type);
    console.log("目标元素:", event.target);
    console.log("事件发生时间:", event.timeStamp);
    
    // 鼠标事件特有属性
    if (event.type === "click") {
        console.log("鼠标位置:", event.clientX, event.clientY);
    }
    
    // 键盘事件特有属性
    if (event.type === "keydown") {
        console.log("按键码:", event.keyCode);
        console.log("是否按下 Ctrl:", event.ctrlKey);
        console.log("是否按下 Shift:", event.shiftKey);
        console.log("是否按下 Alt:", event.altKey);
    }
});

事件传播

事件在 DOM 树中会经历三个阶段:

  1. 捕获阶段:事件从 document 向下传播到目标元素
  2. 目标阶段:事件到达目标元素
  3. 冒泡阶段:事件从目标元素向上传播到 document
html
<div id="parent">
    <button id="child">点击我</button>
</div>
javascript
let parent = document.getElementById("parent");
let child = document.getElementById("child");

// 捕获阶段监听器
parent.addEventListener("click", function() {
    console.log("父元素捕获阶段");
}, true); // 第三个参数为 true 表示在捕获阶段触发

// 冒泡阶段监听器
parent.addEventListener("click", function() {
    console.log("父元素冒泡阶段");
}, false); // 默认为 false,在冒泡阶段触发

child.addEventListener("click", function() {
    console.log("子元素");
});

// 点击按钮时输出顺序:
// 父元素捕获阶段
// 子元素
// 父元素冒泡阶段

阻止事件默认行为

某些事件有默认行为,可以使用 preventDefault() 方法阻止:

javascript
let link = document.querySelector("a");

link.addEventListener("click", function(event) {
    event.preventDefault(); // 阻止链接跳转
    console.log("链接被点击,但不会跳转");
});

let form = document.querySelector("form");

form.addEventListener("submit", function(event) {
    event.preventDefault(); // 阻止表单提交
    console.log("表单提交被阻止");
});

阻止事件传播

可以使用 stopPropagation() 方法阻止事件继续传播:

javascript
let parent = document.getElementById("parent");
let child = document.getElementById("child");

child.addEventListener("click", function(event) {
    event.stopPropagation(); // 阻止事件冒泡
    console.log("子元素");
});

parent.addEventListener("click", function() {
    console.log("父元素"); // 不会执行
});

事件委托

利用事件冒泡机制,可以在父元素上处理子元素的事件:

html
<ul id="list">
    <li>项目 1</li>
    <li>项目 2</li>
    <li>项目 3</li>
</ul>
javascript
let list = document.getElementById("list");

// 使用事件委托处理所有 li 元素的点击事件
list.addEventListener("click", function(event) {
    if (event.target.tagName === "LI") {
        console.log("点击了:", event.target.textContent);
    }
});

// 动态添加的 li 元素也会自动具有事件处理能力
let newLi = document.createElement("li");
newLi.textContent = "项目 4";
list.appendChild(newLi); // 新添加的元素自动具有点击事件处理

移除事件监听器

javascript
function handleClick() {
    console.log("按钮被点击了");
}

// 添加事件监听器
button.addEventListener("click", handleClick);

// 移除事件监听器
button.removeEventListener("click", handleClick);

自定义事件

javascript
// 创建自定义事件
let customEvent = new CustomEvent("myEvent", {
    detail: { message: "自定义事件数据" }
});

// 监听自定义事件
element.addEventListener("myEvent", function(event) {
    console.log("自定义事件被触发:", event.detail.message);
});

// 触发自定义事件
element.dispatchEvent(customEvent);

最佳实践

  1. 优先使用 addEventListener:避免使用内联事件处理器和 DOM 属性事件处理器。

  2. 正确处理 this 绑定

javascript
class Button {
    constructor(element) {
        this.element = element;
        this.clickCount = 0;
        
        // 使用箭头函数保持 this 绑定
        this.element.addEventListener("click", () => {
            this.handleClick();
        });
    }
    
    handleClick() {
        this.clickCount++;
        console.log(`点击次数: ${this.clickCount}`);
    }
}
  1. 及时移除事件监听器:避免内存泄漏。
javascript
function setup() {
    function handler() {
        console.log("处理事件");
    }
    
    button.addEventListener("click", handler);
    
    // 在适当时候移除监听器
    // button.removeEventListener("click", handler);
}
  1. 使用事件委托:对于动态内容或大量相似元素,使用事件委托提高性能。

  2. 阻止不必要的默认行为:只在需要时才调用 preventDefault()

  3. 合理使用事件传播:理解事件传播机制,在适当时候使用捕获或冒泡阶段。

通过掌握事件处理机制,可以创建丰富交互的网页应用,提升用户体验。