news 2026/4/14 11:51:08

JavaScript进阶(四):DOM监听

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaScript进阶(四):DOM监听

文章目录

  • 一.事件监听的三种方式(从旧到新,推荐优先级排序)
    • 1.行内监听(HTML 内联属性,不推荐)
    • 2.DOM 属性监听(元素属性赋值,简单场景可用)
    • 3.addEventListener(标准监听方式,强烈推荐)
  • 二.事件监听的核心概念
    • 1.事件流与监听阶段
    • 2.事件对象(event)的核心作用
  • 三.事件监听的进阶技巧
    • 1.事件委托(代理)
    • 2.高频事件的优化:防抖与节流
      • I.防抖(Debounce)
      • II.节流(Throttle)
    • 3.自定义事件监听
  • 四.事件监听的性能优化
  • 五.常见问题与解决方案
    • 1.匿名函数无法解绑
    • 2.this 指向问题
    • 3.focus/blur 事件不冒泡
    • 4.事件委托中目标元素判断错误
  • 六.总结

DOM事件监听全面详解
事件监听(Event Listener)是DOM交互的核心机制,指为元素注册事件处理函数,当指定事件触发时自动执行该函数.它实现了「行为与结构分离」,是前端处理用户交互、页面状态变化的基础.以下从监听方式、核心配置、进阶技巧、性能优化、常见问题等维度系统拆解.

一.事件监听的三种方式(从旧到新,推荐优先级排序)

1.行内监听(HTML 内联属性,不推荐)

直接在HTML标签中通过on+事件名属性绑定处理函数,是最原始的方式.

语法:

<elementon[事件名]="处理函数(参数)"></element>

示例:

<buttononclick="handleClick('按钮1')">点击我</button><script>functionhandleClick(name){alert(`你点击了${name}`);}</script>

缺点:

  • 耦合度高:HTML结构与JS逻辑混杂,不利于维护;
  • 安全风险: 若拼接用户输入,易引发XSS攻击;
  • 功能有限: 无法绑定多个同类型事件,也无法控制事件流阶段.

2.DOM 属性监听(元素属性赋值,简单场景可用)

通过给DOM元素的on+事件名属性赋值函数,实现事件绑定.

语法:

元素.on[事件名]=处理函数;

示例:

constbtn=document.querySelector('button');// 绑定监听btn.onclick=function(e){console.log('点击事件触发',e.target);};// 解绑监听(赋值为 null 即可)btn.onclick=null;

特点:

  • 优点: 语法简单,兼容性好(支持所有浏览器);
  • 缺点: 只能绑定一个处理函数,重新赋值会覆盖原有函数.

3.addEventListener(标准监听方式,强烈推荐)

W3C制定的标准事件监听API,是现代前端最常用的方式,支持多函数绑定、事件流控制、精细配置.

核心语法:

// 绑定监听元素.addEventListener(事件类型,处理函数,捕获/配置项);// 解绑监听元素.removeEventListener(事件类型,处理函数,捕获/配置项);

关键参数说明:

参数类型说明
事件类型字符串事件名称(如clickinput,不带on前缀)
处理函数函数事件触发时执行的回调(接收事件对象event作为参数)
捕获 / 配置项布尔值 / 对象可选,默认false(冒泡阶段触发);传对象可配置更多选项

基础示例:

constbtn=document.querySelector('button');// 定义处理函数(需命名,方便解绑)constclickHandler=function(e){console.log('点击事件触发',e);};// 绑定监听(冒泡阶段)btn.addEventListener('click',clickHandler);// 绑定多个同类型事件(依次执行)btn.addEventListener('click',()=>{console.log('第二个点击处理函数');});// 解绑监听(必须传同一个函数引用,匿名函数无法解绑)btn.removeEventListener('click',clickHandler);

高级配置项(第三个参数传对象):

btn.addEventListener('scroll',handleScroll,{capture:false,// 是否在**捕获阶段**触发(默认 false,冒泡阶段)once:true,// 事件仅触发一次,触发后自动解绑passive:true,// 禁止处理函数中调用 e.preventDefault()(优化移动端滚动性能)signal:AbortSignal// 通过 AbortController 批量解绑事件(ES2021+)});

signal 配置示例(批量解绑):

constcontroller=newAbortController();const{signal}=controller;// 绑定多个事件,共用同一个 signalbtn.addEventListener('click',()=>console.log('点击 1'),{signal});btn.addEventListener('click',()=>console.log('点击 2'),{signal});// 批量解绑所有绑定的事件controller.abort();

优点:

  1. 支持为同一元素的同一事件绑定多个处理函数;
  2. 可控制事件在捕获 / 冒泡阶段触发;
  3. 提供丰富的配置项(如一次性事件、被动监听);
  4. 支持批量解绑(通过AbortController).

二.事件监听的核心概念

1.事件流与监听阶段

事件在DOM树中传播分为捕获阶段、目标阶段、冒泡阶段,addEventListener的第三个参数决定监听函数在哪个阶段触发:
捕获阶段(capture: true):事件从 window 向下传播到目标元素时触发;
冒泡阶段(capture: false,默认):事件从目标元素向上传播到 window 时触发.
示例:

<divclass="parent"><buttonclass="child">点击</button></div><script>constparent=document.querySelector('.parent');constchild=document.querySelector('.child');// 捕获阶段监听parent.addEventListener('click',()=>console.log('父元素-捕获'),true);// 冒泡阶段监听parent.addEventListener('click',()=>console.log('父元素-冒泡'));child.addEventListener('click',()=>console.log('子元素'));// 点击按钮,执行顺序:父元素-捕获 → 子元素 → 父元素-冒泡</script>

2.事件对象(event)的核心作用

监听函数的第一个参数是事件对象,包含事件的所有关键信息,常用属性 / 方法:

成员作用
e.target事件实际触发的元素(事件源)
e.currentTarget绑定事件的元素(等价于 this)
e.preventDefault()阻止事件的默认行为(如链接跳转、表单提交)
e.stopPropagation()阻止事件继续传播(捕获 / 冒泡)
e.stopImmediatePropagation()阻止事件传播,且同一元素的后续监听函数不再执行

三.事件监听的进阶技巧

1.事件委托(代理)

利用事件冒泡,将子元素的事件监听绑定到父元素,实现「一次绑定,多元素生效」,尤其适用于动态生成的元素.

核心原理:
父元素监听事件后,通过e.target判断触发事件的子元素,执行对应逻辑.

示例(列表项点击监听):

<ulid="list"><li>项1</li><li>项2</li></ul><script>const list = document.getElementById('list'); // 委托父元素绑定点击监听 list.addEventListener('click', (e) => { // 过滤目标元素(仅处理 li 标签) if (e.target.tagName === 'LI') { console.log('点击了列表项:', e.target.textContent); } }); // 动态添加 li,无需重新绑定监听 const newLi = document.createElement('li'); newLi.textContent = '项3'; list.appendChild(newLi);

优点:

  • 减少事件监听的数量,降低内存占用;
  • 支持动态生成的元素,无需重复绑定;
  • 简化代码维护.

2.高频事件的优化:防抖与节流

对于resizescrollinputmousemove等高频触发的事件,直接监听会导致函数频繁执行,引发性能问题,需通过防抖(Debounce) 和节流(Throttle) 优化.

I.防抖(Debounce)

原理:
事件停止触发后,延迟一定时间再执行函数,若期间再次触发则重置延迟.

适用场景:
输入框实时搜索、窗口大小调整.

// 防抖函数functiondebounce(fn,delay=300){lettimer=null;returnfunction(...args){clearTimeout(timer);timer=setTimeout(()=>{fn.apply(this,args);},delay);};}// 使用:输入框监听constinput=document.querySelector('input');input.addEventListener('input',debounce(function(e){console.log('搜索关键词:',e.target.value);},300));

II.节流(Throttle)

原理:
限制函数的执行频率,每隔指定时间仅执行一次.

适用场景:
滚动加载、鼠标拖拽.

// 节流函数functionthrottle(fn,interval=500){letlastTime=0;returnfunction(...args){constnow=Date.now();if(now-lastTime>=interval){fn.apply(this,args);lastTime=now;}};}// 使用:页面滚动监听window.addEventListener('scroll',throttle(function(){console.log('滚动中...');},500));

3.自定义事件监听

除了浏览器内置事件,还可以创建自定义事件,通过dispatchEvent手动触发,适用于组件通信、自定义交互.

示例:

constbtn=document.querySelector('button');// 1. 创建自定义事件(可携带自定义数据)constmyEvent=newCustomEvent('custom-click',{detail:{id:123},// 自定义数据bubbles:true// 允许冒泡});// 2. 绑定自定义事件监听btn.addEventListener('custom-click',(e)=>{console.log('自定义事件触发:',e.detail.id);});// 3. 手动触发自定义事件btn.dispatchEvent(myEvent);

四.事件监听的性能优化

  1. 减少不必要的监听:仅为需要交互的元素绑定监听,避免无意义的监听;
  2. 及时解绑监听:
    • 元素销毁时(如组件卸载),通过removeEventListener解绑监听,避免内存泄漏;
    • 一次性事件使用once: true配置,自动解绑;
  3. 使用事件委托:替代逐个元素绑定,减少监听数量;
  4. 优化高频事件:防抖 / 节流降低函数执行频率;
  5. 移动端优化:
    • 触摸事件(touchstart/touchmove)添加passive: true,提升滚动流畅度;
    • 避免使用click(有 300ms 延迟),可用touchend替代;
  6. 批量解绑:通过AbortController批量管理多个监听,简化解绑逻辑.

五.常见问题与解决方案

1.匿名函数无法解绑

问题:
使用匿名函数绑定的监听,无法通过removeEventListener解绑.

解决:
使用命名函数或保存函数引用.

// 错误:匿名函数无法解绑btn.addEventListener('click',()=>console.log('点击'));btn.removeEventListener('click',()=>console.log('点击'));// 无效// 正确:命名函数consthandler=()=>console.log('点击');btn.addEventListener('click',handler);btn.removeEventListener('click',handler);// 有效

2.this 指向问题

问题:
箭头函数作为监听函数时,this不指向绑定元素(指向外层作用域).

解决:
普通函数的this指向绑定元素,或用e.currentTarget获取.

btn.addEventListener('click',function(){console.log(this);// 指向 btn 元素});btn.addEventListener('click',(e)=>{console.log(e.currentTarget);// 指向 btn 元素(替代 this)});

3.focus/blur 事件不冒泡

问题:
focus/blur事件不支持冒泡,无法使用事件委托.

解决:
使用focusin/focusout 事件(支持冒泡)替代.

// 替代 focusparent.addEventListener('focusin',(e)=>{if(e.target.tagName==='INPUT'){console.log('输入框获取焦点');}});

4.事件委托中目标元素判断错误

问题:
子元素包含嵌套标签时,e.target 可能指向子标签而非目标元素.

解决:
通过向上遍历找到目标元素.

list.addEventListener('click',(e)=>{lettarget=e.target;// 向上遍历,找到 li 元素while(target&&target.tagName!=='LI'){target=target.parentNode;}if(target){console.log('点击了 li:',target.textContent);}});

六.总结

事件监听是前端交互的核心,addEventListener是最推荐的方式,其灵活性和功能性远胜其他方式.掌握事件委托、防抖节流、自定义事件等技巧,能有效提升代码的性能和可维护性.同时,注意及时解绑监听、优化高频事件,可避免内存泄漏和性能问题.

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/12 18:04:32

终极Mac观影神器:打造你的私人美剧影院

终极Mac观影神器&#xff1a;打造你的私人美剧影院 【免费下载链接】iMeiJu_Mac 爱美剧Mac客户端 项目地址: https://gitcode.com/gh_mirrors/im/iMeiJu_Mac 还在为找美剧资源而烦恼吗&#xff1f;在各大视频平台间频繁切换&#xff0c;只为找到心仪的那一部&#xff1f…

作者头像 李华
网站建设 2026/4/7 17:48:11

Linux下通过命令行实现防火墙操作

在Linux系统上管理防火墙&#xff0c;ufw (Uncomplicated Firewall) 是一个非常流行且易于使用的工具&#xff0c;它是 iptables 的一个前端。 &#x1f527; UFW的安装 ufw 通常预装在基于Debian的系统&#xff08;如Ubuntu&#xff09;上。如果你的系统没有&#xff0c;可以使…

作者头像 李华
网站建设 2026/4/13 12:25:05

测试数据管理的自动化工具

被忽视的质量基石 在敏捷开发与持续交付成为主流的今天&#xff0c;测试数据管理&#xff08;TDM&#xff09;仍存在明显滞后性。据行业调研显示&#xff0c;超过67%的软件缺陷源于测试数据问题——数据污染、覆盖不全、环境差异等痛点直接拖累交付周期。本文通过解构自动化工…

作者头像 李华
网站建设 2026/4/9 23:28:35

智能测试数据脱敏技术:保障数据安全与测试效率的工程实践

测试数据管理的困境与破局 在敏捷开发与DevOps普及的今天&#xff0c;软件测试活动日趋频繁。传统的测试数据准备方式——无论是直接使用生产数据的“裸奔”行为&#xff0c;还是耗费大量人力手动编写模拟数据的“作坊”模式——都已无法满足现代软件工程对效率、安全与质量的…

作者头像 李华
网站建设 2026/4/14 9:30:01

MTK设备bootrom保护绕过技术详解:专业级安全解锁方案

MTK设备bootrom保护绕过技术详解&#xff1a;专业级安全解锁方案 【免费下载链接】bypass_utility 项目地址: https://gitcode.com/gh_mirrors/by/bypass_utility MTK芯片设备的安全保护机制一直是手机刷机和系统定制的重要障碍。本工具通过技术手段实现对bootrom保护的…

作者头像 李华