JavaScript 事件处理

事件是用户与页面交互的桥梁——点击按钮、敲击键盘、滚动页面,每个动作都是一个事件(event)。JavaScript 通过事件监听器来响应用户操作。

addEventListener() — 监听事件

javascript
const button = document.querySelector('button');

button.addEventListener('click', () => {
  console.log('按钮被点击了!');
});

语法:元素.addEventListener('事件名', 回调函数)

同一个元素可以绑定多个监听器,它们会按注册顺序执行:

javascript
button.addEventListener('click', () => console.log('第一个'));
button.addEventListener('click', () => console.log('第二个'));
// 点击时输出:第一个  第二个

常用事件类型

鼠标事件

事件触发时机
click鼠标单击(最常用)
dblclick鼠标双击
mousedown按下鼠标键
mouseup松开鼠标键
mouseenter鼠标进入元素(不冒泡)
mouseleave鼠标离开元素(不冒泡)
mousemove鼠标在元素上移动

键盘事件

事件触发时机
keydown键盘按下(持续按住会持续触发)
keyup键盘松开
keypress字符键按下(已过时,用 keydown

表单事件

事件触发时机
input输入框内容发生变化(每输入一个字符)
change表单控件值改变并失去焦点
submit表单提交
focus元素获得焦点
blur元素失去焦点

其他常用事件

事件触发时机
scroll页面或元素滚动
resize窗口大小改变
DOMContentLoadedDOM 构建完成
load页面完全加载(含图片)

事件对象

事件监听器的回调函数接收一个事件对象,包含事件的所有信息:

javascript
button.addEventListener('click', (event) => {
  console.log(event.type);      // "click"
  console.log(event.target);    // 被点击的元素
  console.log(event.clientX);   // 鼠标 x 坐标
  console.log(event.clientY);   // 鼠标 y 坐标
});

键盘事件对象

javascript
document.addEventListener('keydown', (event) => {
  console.log(event.key);  // "a", "Enter", "Escape"
  console.log(event.code); // "KeyA", "Enter", "Escape"(物理键位)

  if (event.key === 'Escape') {
    closeModal();
  }
  if (event.ctrlKey && event.key === 's') {
    event.preventDefault(); // 阻止浏览器默认的保存行为
    saveDocument();
  }
});

阻止默认行为

部分元素有默认行为(链接跳转、表单提交、右键菜单),可以用 preventDefault() 阻止:

javascript
// 阻止链接跳转(常用于单页应用)
link.addEventListener('click', (event) => {
  event.preventDefault();
  navigateTo(link.href);
});

// 阻止表单的默认提交
form.addEventListener('submit', (event) => {
  event.preventDefault();
  // 自行处理数据
  const formData = new FormData(form);
});

事件委托(Event Delegation)

当有大量子元素需要相同的事件处理时,将监听器绑定在父元素上,利用事件冒泡机制判断实际点击的目标:

javascript
// ❌ 为每个列表项绑定监听器(如果有 1000 个 li,太浪费)
document.querySelectorAll('li').forEach(li => {
  li.addEventListener('click', handleClick);
});

// ✅ 事件委托:只在父元素绑定一个监听器
const list = document.querySelector('ul');
list.addEventListener('click', (event) => {
  if (event.target.tagName === 'LI') {
    console.log('点击了:', event.target.textContent);
  }
});

事件委托 + 新增元素

事件委托的另一个关键优势:即使是动态添加的子元素,也会被父元素的监听器捕获

javascript
list.addEventListener('click', (event) => {
  if (event.target.tagName === 'LI') {
    event.target.classList.toggle('selected');
  }
});

// 稍后动态添加的 li 也会响应点击
list.insertAdjacentHTML('beforeend', '<li>新增项目</li>');

事件委托是 DOM 编程中最重要的性能优化模式之一。大幅减少监听器数量,天然支持动态元素。只要场景允许,优先使用事件委托而非逐个绑定。

移除监听器

javascript
function handleClick(event) {
  console.log('点击');
}

button.addEventListener('click', handleClick);
button.removeEventListener('click', handleClick); // 移除

removeEventListener 需要传入同一个函数引用。匿名箭头函数无法被移除——removeEventListener('click', () => {...}) 永远不会工作,因为它是一个新的函数对象。