JavaScript进阶(三):DOM事件

文章目录

DOM 事件是前端交互的核心,指浏览器或用户触发的各类行为(如 点击、滚动、输入 ),通过「事件绑定 - 事件触发 - 事件处理」的流程实现页面交互逻辑.

一.事件核心概念

概念 说明
事件源(target) 触发事件的 DOM 元素(如点击的按钮、输入的输入框)
事件类型(type) 事件的类别(如 clickinputscroll)
事件处理函数(handler) 事件触发后执行的函数(也称回调函数)
事件对象(event) 事件触发时自动传入处理函数的参数,包含事件的所有信息(如坐标、触发源、按键)
事件流 事件在 DOM 树中的传播过程(捕获阶段 → 目标阶段 → 冒泡阶段)

二.常见事件类型(按场景分类)

1.鼠标事件

最常用的交互事件,适用于点击、悬浮、拖拽等场景:

事件名 触发时机 常用场景
click 鼠标左键单击(按下 + 松开) 按钮点击、链接跳转
dblclick 鼠标左键双击 双击编辑、双击放大
mousedown 鼠标按键按下(任意键) 拖拽开始、按住操作
mouseup 鼠标按键松开(任意键) 拖拽结束、释放操作
mousemove 鼠标在元素内移动 拖拽跟随、鼠标轨迹
mouseover 鼠标移入元素(含子元素,会冒泡) 悬浮提示(慎用,推荐 mouseenter)
mouseenter 鼠标移入元素(不含子元素,不冒泡) 悬浮菜单、卡片高亮
mouseout 鼠标移出元素(含子元素,会冒泡) 悬浮提示消失
mouseleave 鼠标移出元素(不含子元素,不冒泡) 悬浮菜单收起
contextmenu 鼠标右键单击 自定义右键菜单

2.键盘事件

适用于键盘操作(输入、快捷键):

事件名 触发时机 关键属性(event)
keydown 键盘按键按下(持续按会重复触发) key(按键名,如 Enter)、 code(按键编码)、 ctrlKey/shiftKey(修饰键)
keyup 键盘按键松开 同上
keypress 按键按下且产生字符(已废弃,用 keydown 替代) -

3.表单事件

适用于表单元素交互:

事件名 触发时机 适用元素
input 表单值变化(实时触发) input / textarea / select
change 表单值变化且失去焦点(或下拉框选中) 同上
focus 元素获取焦点(不冒泡) 所有表单元素、可聚焦元素(如 divtabindex)
blur 元素失去焦点(不冒泡) 同上
submit 表单提交(点击提交按钮 / 按 Enter) form 元素
reset 表单重置 form 元素

4.页面 / 窗口事件

适用于页面加载、尺寸变化、滚动等:

事件名 触发时机 注意事项
load 页面 / 资源(img/script)加载完成 window.onload:页面所有资源加载完成;img.onload:单张图片加载完成
DOMContentLoaded DOM 解析完成(无需等待资源加载) load 早触发,优先用于初始化逻辑
resize 窗口 / 元素尺寸变化 高频事件,需防抖
scroll 页面 / 元素滚动 高频事件,需防抖 / 节流
unload 页面卸载(关闭 / 跳转) 慎用,执行时机不可靠,推荐 beforeunload
beforeunload 页面即将卸载 可提示用户 "是否离开"

5.触摸事件(移动端)

适用于手机 / 平板的触摸操作:

事件名 触发时机 关键属性(event)
touchstart 手指触摸屏幕 touches(所有触摸点)、 targetTouches(当前元素触摸点)
touchmove 手指在屏幕上滑动 同上,可获取滑动坐标
touchend 手指离开屏幕 同上
touchcancel 触摸被中断(如弹窗、电话) -

三.事件绑定方式(优先级:推荐 ③ > ② > ①)

1.行内绑定(原生 HTML,不推荐)

直接写在 HTML 标签中,耦合度高,不利于维护:

html 复制代码
<button onclick="handleClick()">点击</button>

<script>
  function handleClick() {
    alert('点击了按钮');
  }
</script>

⚠️ 缺点:无法绑定多个同类型事件、易引发 XSS、代码分离性差.

2.DOM 属性绑定(简单场景可用)

通过元素属性赋值绑定,只能绑定一个处理函数:

javascript 复制代码
const btn = document.querySelector('button');
// 绑定
btn.onclick = function (e) {
  console.log('点击事件', e.target); // e:事件对象
};
// 解绑
btn.onclick = null;

⚠️ 缺点:覆盖原有事件(重新赋值会替换之前的处理函数).

3.addEventListener(推荐,标准方式)

W3C 标准,支持绑定多个处理函数,可控制事件流阶段:

javascript 复制代码
const btn = document.querySelector('button');
// 绑定:参数(事件类型,处理函数,是否捕获/配置项)
const handler = function (e) {
  console.log('点击事件', e);
};
btn.addEventListener('click', handler);
// 绑定多个同类型事件(依次执行)
btn.addEventListener('click', () => {
  console.log('第二个点击处理函数');
});

// 解绑:必须传绑定的同一个函数(匿名函数无法解绑)
btn.removeEventListener('click', handler);

// 配置项(第三个参数可传对象,替代布尔值)
btn.addEventListener('scroll', handleScroll, {
  capture: false, // 否在捕获阶段触发(默认 false,冒泡阶段)
  once: true, // 事件仅触发一次(自动解绑)
  passive: true // 禁止阻止默认行为(优化移动端滚动性能)
});

✅ 优点:支持多函数绑定、精准解绑、控制事件流、丰富配置项.

四.事件对象(event)核心属性 / 方法

事件处理函数的第一个参数是事件对象,包含事件的所有关键信息:

属性 / 方法 作用 示例
target 事件实际触发的元素(事件源) e.target(冒泡时不会变)
currentTarget 绑定事件的元素(this 等价) e.currentTarget(冒泡时指向当前处理的元素)
type 事件类型(如 clickinput) e.type // "click"
clientX / clientY 鼠标相对于视口的坐标(不含滚动) 鼠标点击位置:e.clientX + 'px'
pageX / pageY 鼠标相对于文档的坐标(含滚动) -
key 键盘事件的按键名(如 Entera) if (e.key === 'Enter') { 提交表单 }
ctrlKey / shiftKey / altKey 是否按下修饰键(布尔值) if (e.ctrlKey && e.key === 's') { 保存 }
preventDefault() 阻止事件默认行为 阻止链接跳转:e.preventDefault()
stopPropagation() 阻止事件冒泡 / 捕获 阻止父元素触发同类型事件
stopImmediatePropagation() 阻止事件传播 + 同元素后续处理函数 绑定多个 click 函数时,后续函数不执行
bubbles 事件是否可冒泡(布尔值) e.bubbles // true(如 click 可冒泡,focus 不可)

五.事件流(捕获 vs 冒泡)

事件触发后,会在 DOM 树中经历三个阶段(W3C 标准):

  1. 捕获阶段:从 window → 文档 → 父元素 → 目标元素(从上到下);
  2. 目标阶段:事件到达实际触发的元素;
  3. 冒泡阶段:从目标元素 → 父元素 → 文档 → window(从下到上).

核心示例(冒泡 vs 捕获)

html 复制代码
<div class="parent" style="padding: 20px; background: #eee;">
  <button class="child">点击</button>
</div>

<script>
  const parent = document.querySelector('.parent');
  const child = document.querySelector('.child');

  // 冒泡阶段触发(默认)
  parent.addEventListener('click', () => console.log('父元素-冒泡'));
  child.addEventListener('click', () => console.log('子元素-冒泡'));

  // 捕获阶段触发(第三个参数为true)
  parent.addEventListener('click', () => console.log('父元素-捕获'), true);
  child.addEventListener('click', () => console.log('子元素-捕获'), true);

  // 点击按钮,执行顺序:
  // 父元素-捕获 → 子元素-冒泡 → 子元素-捕获 → 父元素-冒泡
  // (目标阶段不分捕获/冒泡,按绑定顺序执行)
</script>

六.事件进阶技巧

1.事件委托(事件代理)

利用事件冒泡,将子元素的事件绑定到父元素,减少事件绑定数量,优化性能(尤其适用于动态生成的元素):

html 复制代码
<ul id="list">
  <li>项1</li>
  <li>项2</li>
  <!-- 动态添加的li也能触发事件 -->
</ul>

<script>
  const list = document.getElementById('list');
  // 委托父元素绑定事件
  list.addEventListener('click', (e) => {
    // 过滤目标元素(仅处理li)
    if (e.target.tagName === 'LI') {
      console.log('点击了li:', e.target.textContent);
    }
  });

  // 动态添加li,无需重新绑定事件
  const newLi = document.createElement('li');
  newLi.textContent = '项3';
  list.appendChild(newLi);
</script>

✅ 优点:减少事件绑定、支持动态元素、降低内存占用.

2.防抖(Debounce)

解决高频事件(如 resizescrollinput)频繁触发的问题,仅在事件停止触发后执行一次:

javascript 复制代码
运行;
// 防抖函数
function debounce(fn, delay = 300) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

// 使用:输入框实时搜索
const input = document.querySelector('input');
input.addEventListener(
  'input',
  debounce(function (e) {
    console.log('搜索:', e.target.value); // 停止输入 300ms 后执行
  }, 300)
);

3.节流(Throttle)

限制高频事件的执行频率,每隔指定时间仅执行一次:

javascript 复制代码
运行;
// 节流函数
function throttle(fn, interval = 500) {
  let lastTime = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastTime >= interval) {
      fn.apply(this, args);
      lastTime = now;
    }
  };
}

// 使用:滚动加载
window.addEventListener(
  'scroll',
  throttle(function () {
    console.log('滚动中...'); // 每 500ms 执行一次
  }, 500)
);

4.自定义事件

手动创建 / 触发事件,适用于组件通信、自定义交互:

javascript 复制代码
// 1. 创建自定义事件
const myEvent = new CustomEvent('custom-click', {
  detail: { id: 123 }, // 自定义数据
  bubbles: true, // 允许冒泡
  cancelable: true // 允许阻止默认行为
});

// 2. 绑定自定义事件
const btn = document.querySelector('button');
btn.addEventListener('custom-click', (e) => {
  console.log('自定义事件触发:', e.detail.id); // 输出 123
});

// 3. 手动触发事件
btn.dispatchEvent(myEvent);
相关推荐
丫丫7237342 小时前
相机动画总结-相机直线运动动画、相机圆周运动动画
javascript·webgl
Evand J2 小时前
【MATLAB例程】自适应阈值的小波变换去噪,信号噪声:拉普拉斯噪声、脉冲噪声与高斯噪声|混合非高斯。附下载链接
开发语言·人工智能·matlab
全栈陈序员2 小时前
【Python】基础语法入门(十八)——函数式编程初探:用 `map`、`filter`、`reduce` 和 `lambda` 写出更简洁的代码
开发语言·人工智能·python·学习
哆啦A梦15882 小时前
商城后台管理系统 06,编辑商品
javascript·vue.js·elementui
qq_406176142 小时前
JavaScript中的循环特点和区别
开发语言·javascript·ecmascript
我命由我123453 小时前
Python 开发 - OpenAI 兼容阿里云百炼平台 API
开发语言·人工智能·后端·python·阿里云·ai·语言模型
GokuCode3 小时前
【GO高级编程】02.GO接收者概述
开发语言·后端·golang
Aotman_3 小时前
JavaScript去除对象字段空格
开发语言·前端·javascript