人人都会AI编程

15.3 事件对象:常用属性与方法、阻止默认行为、阻止冒泡

更新时间:2026-07-11

当一个事件被触发时,浏览器会生成一个事件对象(Event Object),并作为第一个参数传入事件处理函数。这个对象携带着关于此次事件的所有关键信息——谁触发的、在哪里触发的、什么类型的事件、按下的是哪个键……理解事件对象,才能精确控制交互行为。

15.3.1 获取事件对象

事件对象会自动传入,只需在处理函数中声明一个参数接收即可:

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

// 箭头函数同样适用
button.addEventListener('click', (event) => {
  console.log(event);
});

这个 event 参数是实打实的对象,不是可有可无的装饰。下面来看看它身上最常用的属性。

15.3.2 常用属性

event.type

事件类型,字符串形式。例如 'click''keydown''submit'。当一个处理函数被绑定到多种事件时,可以用 type 区分当前是哪种事件触发了回调。

element.addEventListener('mouseenter mouseleave', (e) => {
  if (e.type === 'mouseenter') {
    console.log('鼠标进入');
  } else {
    console.log('鼠标离开');
  }
});

event.target

事件的真正触发者。即用户实际点击的那个 DOM 元素。这个属性在事件冒泡过程中始终指向最初触发事件的元素,不会改变

event.currentTarget

当前正在处理该事件的元素。也就是绑定事件处理函数的那个元素。在事件冒泡过程中,currentTarget 会随着事件的向上传递而变化——它始终等于 this(在非箭头函数中)。

这两个属性在事件委托中尤为重要,我们来看一个具体例子:

<ul id="list">
  <li>项目 1</li>
  <li>项目 2</li>
  <li>项目 3</li>
</ul>
const ul = document.getElementById('list');
ul.addEventListener('click', function(e) {
  console.log(e.target);       // 用户具体点击的那个 <li>
  console.log(e.currentTarget); // 始终是 <ul>,因为事件绑定在这里
});

如果点击“项目 2”,输出:

  • e.target<li>项目 2</li>
  • e.currentTarget<ul id="list">

这个区别是利用事件委托精确处理子元素事件的基础。

event.clientX / event.clientY

鼠标事件发生时,鼠标指针相对于浏览器视口(viewport)的水平和垂直坐标。如果你需要知道用户点击了页面的哪个位置,这是最直接的方式。

event.keyevent.code

键盘事件专用。key 返回按键的显示值(如 'a''Enter''ArrowUp'),而 code 返回按键的物理位置代码(如 'KeyA''Enter''ArrowUp')。前者受键盘布局影响,后者不受,更适合游戏等需要固定按键位置的场景。

document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape') {
    closeModal();
  }
});

event.preventDefault() 是否存在

event.defaultPrevented 属性表示是否已经调用了 preventDefault(),在为复杂交互做条件判断时非常实用。

15.3.3 常用方法

event.preventDefault()

阻止浏览器的默认行为。什么是默认行为?点击一个链接会跳转页面,点击提交按钮会触发表单提交,按下空格键会向下滚动页面,这些就是浏览器为某些元素预置的默认行为。调用此方法后,这些行为不会发生。

// 阻止链接跳转
document.querySelector('a').addEventListener('click', (e) => {
  e.preventDefault();
  console.log('链接被点击,但不会跳转');
});

// 阻止表单提交
document.querySelector('form').addEventListener('submit', (e) => {
  e.preventDefault();
  // 用 JS 自行处理表单数据,比如通过 fetch 发送
});

注意preventDefault() 不影响事件的传播(冒泡),它只是屏蔽了浏览器对当前事件的默认反应。

event.stopPropagation()

阻止事件继续冒泡或捕获。调用后,事件不会继续传递给父级或子级元素(取决于所处阶段),但在当前元素上如果有多个同类型事件处理函数,它们仍会全部执行。

document.querySelector('.child').addEventListener('click', (e) => {
  e.stopPropagation();
  console.log('子元素点击,事件不会冒泡到父元素');
});

document.querySelector('.parent').addEventListener('click', () => {
  console.log('父元素被点击(冒泡上来的)');
});

// 点击子元素时,只会输出子元素的 log,父元素的事件不会触发

event.stopImmediatePropagation()

stopPropagation() 更强硬。它不仅阻止事件冒泡,还阻止当前元素上绑定的其他同类型事件处理函数执行。当一个元素绑定了多个 click 处理程序,而你又希望某个处理函数成为“终点”时,这个方法非常有用。

button.addEventListener('click', (e) => {
  e.stopImmediatePropagation();
  console.log('第一个处理函数,并阻止其他处理函数执行');
});

button.addEventListener('click', () => {
  console.log('第二个处理函数,永远不会被调用');
});

通常情形下我们很少需要这么极端的控制,但了解它的存在可以在遇到怪异的执行顺序时多一个排查方向。

15.3.4 阻止默认行为 vs 阻止冒泡:一张对比表

| 方法 | 作用 | 影响范围 | 典型场景 |
|------|------|----------|----------|
| preventDefault() | 阻止浏览器预设行为 | 仅当前事件,不阻止传播 | 阻止链接跳转、表单提交、拖拽默认行为 |
| stopPropagation() | 阻止事件冒泡/捕获 | 阻止事件传递给祖先/后代 | 避免子元素点击触发父容器事件 |
| stopImmediatePropagation() | 阻止冒泡 + 阻止同元素其他处理函数 | 阻止后续所有同类事件处理 | 按钮只允许某一个处理函数生效 |

两个方法可以结合使用,互不冲突:

link.addEventListener('click', (e) => {
  e.preventDefault();      // 不跳转
  e.stopPropagation();     // 也不冒泡给父级菜单
  // 执行自定义逻辑...
});

15.3.5 实战提醒

  1. 不要滥用 stopPropagation():它切断了事件流,可能会让上层的事件委托失效,导致全局性的行为(如关闭弹窗)无法正常工作。大多数情况下,通过 e.target 判断点击来源即可精确处理,无需完全阻断冒泡。
  1. preventDefault() 并非总是有效:某些事件是不可取消的(cancelable: false),比如 scroll 事件的默认行为就无法用此方法阻止。可以在事件对象上查看 cancelable 属性:e.cancelable === true 时才能调用 preventDefault()
  1. 在事件委托中依靠 target 而非阻断:委托场景下,父元素需要捕获子元素冒上来的事件,此时调用 stopPropagation() 等于自毁长城。正确的做法是:
parent.addEventListener('click', (e) => {
  if (e.target.matches('.btn')) {
    // 处理子按钮的点击
  }
});

这样既不破坏事件流,又能精确响应所需元素。

掌握事件对象的核心属性和方法,并结合事件流的三个阶段,你就能在任何复杂的嵌套交互中做到精确控制,既不误触发,也不漏触发。这是实现可靠交互体验的基石,也是下一节深入事件委托与自定义事件的理论前提。