news 2026/5/11 11:41:46

JavaScript DOM事件监听实战:从addEventListener到removeEventListener的进阶应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaScript DOM事件监听实战:从addEventListener到removeEventListener的进阶应用

1. 初识DOM事件监听:从点击按钮开始

记得我第一次接触JavaScript事件监听时,就像发现新大陆一样兴奋。那时候我只会用onclick属性,直到发现addEventListener这个神奇的方法。举个最简单的例子,给按钮添加点击事件:

const button = document.getElementById('myButton'); button.addEventListener('click', function() { alert('按钮被点击了!'); });

这个方法比直接写onclick好在哪里呢?首先,它不会覆盖已有的事件处理程序。想象你正在开发一个多人协作的项目,同事在同一个按钮上绑定了他的逻辑,如果你用onclick就会把他的代码覆盖掉,而addEventListener则会让两个事件处理程序都生效。

另一个优势是更灵活的事件控制。比如第三个参数useCapture,可以决定事件是在捕获阶段还是冒泡阶段触发。虽然大多数情况下我们用默认的冒泡模式(false),但在某些特殊场景下,捕获模式(true)就派上用场了。

2. 进阶事件处理:从简单到复杂

2.1 事件对象与事件委托

每个事件处理函数都会自动接收一个事件对象参数,这个对象包含了事件的详细信息:

document.addEventListener('click', function(event) { console.log(`点击位置:X=${event.clientX}, Y=${event.clientY}`); console.log(`触发元素:`, event.target); });

事件委托是个超级实用的技巧。比如有个动态列表,每个列表项都要有点击事件,与其给每个li都绑定事件,不如直接在父元素上绑定:

const list = document.getElementById('myList'); list.addEventListener('click', function(event) { if(event.target.tagName === 'LI') { console.log('点击了列表项:', event.target.textContent); } });

2.2 性能优化:防抖与节流

在处理频繁触发的事件(如scroll、resize、mousemove)时,防抖(debounce)和节流(throttle)是必备技能:

// 防抖:事件结束后才执行 function debounce(fn, delay) { let timer; return function() { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, arguments), delay); }; } // 节流:固定时间间隔执行一次 function throttle(fn, interval) { let lastTime = 0; return function() { const now = Date.now(); if(now - lastTime >= interval) { fn.apply(this, arguments); lastTime = now; } }; } window.addEventListener('resize', debounce(function() { console.log('窗口大小变化结束'); }, 300));

3. 事件移除的艺术:removeEventListener详解

很多人会忽略removeEventListener的重要性,直到遇到内存泄漏问题。移除事件监听有几个关键点:

  1. 必须使用相同的函数引用
  2. 必须匹配相同的useCapture参数值
// 正确做法:保存函数引用 const handler = function() { console.log('处理点击'); }; button.addEventListener('click', handler, false); // 之后可以这样移除 button.removeEventListener('click', handler, false); // 错误做法:匿名函数无法移除 button.addEventListener('click', function() { console.log('这个监听器无法被移除'); });

在单页应用(SPA)中,组件卸载时一定要记得移除事件监听,否则会导致内存泄漏。我曾经遇到过因为忘记移除window上的resize监听,导致页面切换后旧组件无法被垃圾回收的情况。

4. 实战案例:拖拽功能的完整实现

让我们用事件监听实现一个完整的拖拽功能:

const draggable = document.getElementById('draggable'); let isDragging = false; let offsetX, offsetY; draggable.addEventListener('mousedown', startDrag); function startDrag(e) { isDragging = true; offsetX = e.clientX - draggable.getBoundingClientRect().left; offsetY = e.clientY - draggable.getBoundingClientRect().top; document.addEventListener('mousemove', drag); document.addEventListener('mouseup', stopDrag); } function drag(e) { if(!isDragging) return; draggable.style.left = `${e.clientX - offsetX}px`; draggable.style.top = `${e.clientY - offsetY}px`; } function stopDrag() { isDragging = false; document.removeEventListener('mousemove', drag); document.removeEventListener('mouseup', stopDrag); }

这个例子展示了多个事件监听器的协同工作,以及在适当时候移除监听器的重要性。注意我们在document上而不是拖拽元素本身监听mousemove,这样可以避免鼠标移动过快时丢失拖拽的问题。

5. 特殊场景与注意事项

5.1 被动事件监听器

对于某些高频事件,可以使用passive选项来提升滚动性能:

// 提升滚动性能 document.addEventListener('touchmove', function(e) { // 这里不能调用e.preventDefault() }, { passive: true });

5.2 一次性事件监听

如果只需要监听事件一次,可以使用{once: true}选项:

button.addEventListener('click', function() { console.log('这个监听器只会执行一次'); }, { once: true });

5.3 自定义事件

除了内置事件,我们还可以创建和触发自定义事件:

// 创建自定义事件 const event = new CustomEvent('myEvent', { detail: { message: '这是自定义数据' } }); // 监听自定义事件 document.addEventListener('myEvent', function(e) { console.log(e.detail.message); }); // 触发事件 document.dispatchEvent(event);

在实际项目中,我发现合理使用事件监听可以让代码更加模块化和可维护。关键是要记住:有添加就要有移除,特别是在动态内容中。曾经有个项目因为忘记移除事件监听,导致页面内存占用越来越高,最后不得不重构相关代码。

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

3个神奇技巧:让闲置电视盒子变身全功能Linux服务器

3个神奇技巧:让闲置电视盒子变身全功能Linux服务器 【免费下载链接】amlogic-s9xxx-armbian Supports running Armbian on Amlogic, Allwinner, and Rockchip devices. Support a311d, s922x, s905x3, s905x2, s912, s905d, s905x, s905w, s905, s905l, rk3588, rk3…

作者头像 李华
网站建设 2026/5/11 11:35:53

信息学奥赛一本通 1040:输出绝对值 | OpenJudge NOI 1.4 02

1. 从零开始理解绝对值计算 绝对值是数学中一个非常基础但又极其重要的概念。简单来说,一个数的绝对值就是它在数轴上与原点的距离,永远是非负的。比如-5的绝对值是5,3的绝对值还是3。在编程竞赛和日常开发中,处理绝对值是非常常见…

作者头像 李华
网站建设 2026/5/11 11:32:31

2023B卷,太阳能航天器

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:华为OD面试 文章目录 一、🍀前言 1.1 ☘️题目详情 1.2 ☘️参考解题答案 一、🍀前言 2023B卷,太阳能航天器。 1.1 ☘️题目详情 题目: 给航天器…

作者头像 李华
网站建设 2026/5/11 11:31:33

如何快速安装HS2汉化补丁:完整游戏优化指南

如何快速安装HS2汉化补丁:完整游戏优化指南 【免费下载链接】HS2-HF_Patch Automatically translate, uncensor and update HoneySelect2! 项目地址: https://gitcode.com/gh_mirrors/hs/HS2-HF_Patch HS2-HF Patch是HoneySelect2玩家的终极解决方案&#xf…

作者头像 李华
网站建设 2026/5/11 11:30:39

如何通过Sunshine配置文件打造极致游戏串流体验

如何通过Sunshine配置文件打造极致游戏串流体验 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 作为一款开源的自托管游戏串流服务器,Sunshine让您能够通过Moonlight客…

作者头像 李华
网站建设 2026/5/11 11:27:36

[具身智能-656]:人的大脑天然的多模态神经网络,天然的多模态大模型!

人脑:天生的多模态神经网络、原生多模态大模型 深度详解 一、先定核心定义 人脑从出生起,就是硬件固化、无训练成本、原生融合、端侧离线、超低功耗的多模态神经网络大模型。它不是后天编程搭建,而是生物进化形成的天然多模态感知 - 融合 -…

作者头像 李华