news 2026/2/3 8:27:36

小白前端别懵圈:搞懂事件表,再也不怕点击没反应了!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
小白前端别懵圈:搞懂事件表,再也不怕点击没反应了!


小白前端别懵圈:搞懂事件表,再也不怕点击没反应了!

  • 小白前端别懵圈:搞懂事件表,再也不怕点击没反应了!
    • 为啥你点按钮像打空气?
    • 事件表到底藏哪儿了?
    • 浏览器背后的小本本:事件表是啥玩意儿
    • 这东西好用归好用,但坑也不少
      • 内存泄漏大户:忘了 removeEventListener,事件表就成垃圾场
      • 重复绑定?事件表可不会自动去重
      • 移动端 touch 和 click 混着绑?事件表直接给你表演“双倍快乐”
    • 真实项目里怎么玩转事件表
      • 动态列表里的按钮怎么绑事件才不崩?
      • 框架时代(React/Vue)还用管事件表吗?
    • 监听器不生效?先别砸键盘
      • 是不是作用域搞错了?this 指向翻车现场
      • 是不是元素还没渲染完你就急着绑?
      • 是不是用了箭头函数结果丢了 event 对象?
    • 几个骚操作让你写得更稳
      • 统一管理事件监听器,自己建个“伪事件表”做登记注销
      • 开发阶段加个 console.log 打印当前绑了多少监听,防爆炸
      • 用 AbortController 一键清除多个监听,告别 removeEventListener 地狱
    • 下次再遇到“点了没反应”,别只会刷新页面了

小白前端别懵圈:搞懂事件表,再也不怕点击没反应了!


为啥你点按钮像打空气?

先别急着骂浏览器,也别摔鼠标。
你刚写完一个按钮,信心满满地刷新页面,手指一点——啪!空气。
控制台干净得像你刚擦过的 MacBook 屏幕,没有任何报错。
你怀疑人生:我写的addEventListener被狗吃了?

别慌,这不是灵异事件,也不是你电脑被产品经理远程下了降头。
真相只有一个:事件表把你耍了。


事件表到底藏哪儿了?

先给你打个预防针:
事件表不是一张 Excel,也不是window.eventTable,更不是 DOM 节点上的属性。
它藏在浏览器的 C++ 底层,属于“内核大佬的私人日记”。
JS 层面你看不见、摸不着,但它比你的 ex 还记仇。

当你写下:

btn.addEventListener('click',()=>alert('hello'));

浏览器内核立刻在日记里记一笔:

“btn 元素,click 事件,回调地址 0x7f3e4a2b,捕获 false,一次 false,优先级 0。”

下次你点按钮,内核翻日记:“哦,这哥们有登记”,于是把回调扔进事件队列,等主线程空了再执行。
整个流程像极了你去网红店排队:先拿号(注册),再等叫号(队列),最后吃上了(执行)。


浏览器背后的小本本:事件表是啥玩意儿

为了让你感受更直观,我直接给你看一段“伪源码”——
注意,是伪的,别真去 Chromium 里搜,搜到你秃头也搜不到。

// 伪代码:Blink 内核里的简化事件表classEventTarget{// key: 事件类型字符串 "click"// value: 监听器数组,按绑定顺序排队HashMap<String,Vector<Listener>>listenerMap;};structListener{void*callback;// 函数指针booluseCapture;// 是否捕获阶段boolonce;// 是否只执行一次AbortSignal*signal;// 可选的 AbortController 信号};

看到没?本质就是一个哈希表 + 数组
你每addEventListener一次,就往数组里push一个结构体。
重复绑定?数组就膨胀,浏览器不会帮你去重。
所以点一次触发三次,真不是你手抖,是你代码手抖。


这东西好用归好用,但坑也不少

内存泄漏大户:忘了 removeEventListener,事件表就成垃圾场

很多小伙伴写完组件直接xxx = null,以为 GC 会扫地。
但内核的小本本上还有引用,闭包里的变量一样被按住,内存就 leak 了
像极了你分手后还留着对方的微信,结果每晚翻朋友圈emo。

正确姿势:
统一注销,别偷懒。

functionsetup(){consthandler=()=>console.log('click');btn.addEventListener('click',handler);// 返回清理函数,爱啥时候调就啥时候调return()=>btn.removeEventListener('click',handler);}constcleanup=setup();// 组件销毁时cleanup();// 干净利落,不留痕迹

重复绑定?事件表可不会自动去重

有人图方便,在useEffectmounted里重复执行:

// React 例子useEffect(()=>{window.addEventListener('resize',handleResize);},[handleResize]);// 依赖一变就重新绑一次

结果 resize 的时候触发 N 次,风扇转得比直升机还响。
解决思路:先清再绑,或者只绑一次。

useEffect(()=>{window.addEventListener('resize',handleResize);return()=>window.removeEventListener('resize',handleResize);},[]);// 空依赖,只绑一次

移动端 touch 和 click 混着绑?事件表直接给你表演“双倍快乐”

移动浏览器为了兼容,会在touchend模拟一次 click,俗称“幽灵点击”。
如果你同时绑了touchstartclick就可能触发两次
解决办法:

  1. 只用touchstart+preventDefault把模拟 click 吃掉;
  2. 或者直接用pointerup,一劳永逸。
btn.addEventListener('touchstart',e=>{e.preventDefault();// 吃掉幽灵点击doSomething();});

真实项目里怎么玩转事件表

动态列表里的按钮怎么绑事件才不崩?

场景:你渲染了 1000 行表格,每行都有“删除”按钮。
傻办法:循环 1000 次addEventListener,手机直接变暖手宝。
聪明办法:事件委托,把事件表压缩成一条记录。

// 只绑一次,挂在父级table.addEventListener('click',e=>{// 利用 dataset 识别目标if(e.target.dataset.action==='delete'){constid=e.target.dataset.id;deleteRow(id);}});

优点:

  1. 无论新增还是删除行,都不需要重新绑;
  2. 事件表只有一条记录,内存稳;
  3. 代码少,产品经理挑不出毛病。

框架时代(React/Vue)还用管事件表吗?

React 的onClick={foo}看起来人畜无害,其实底层还是addEventListener,只是 React 帮你做了合成事件池(SyntheticEvent)。
Vue 的@click也一样,最终都会登记到浏览器的小本本。
框架不管注销,你自己得管。

// React 函数组件,防止内存泄漏 useEffect(() => { const handler = e => { if (e.key === 'Escape') closeModal(); }; document.addEventListener('keydown', handler); return () => document.removeEventListener('keydown', handler); }, []);

监听器不生效?先别砸键盘

是不是作用域搞错了?this 指向翻车现场

classCounter{count=0;constructor(){btn.addEventListener('click',this.add);// 完蛋,this 丢了}add(){this.count++;// this 指向 button,不是 Counter}}

解决:

btn.addEventListener('click',this.add.bind(this));// 或者箭头函数btn.addEventListener('click',()=>this.add());

是不是元素还没渲染完你就急着绑?

// 错误示范:元素都没出生就绑document.querySelector('#future-btn').addEventListener('click',handler);

正确姿势:等 DOM 就位。

window.addEventListener('DOMContentLoaded',()=>{document.querySelector('#future-btn').addEventListener('click',handler);});

是不是用了箭头函数结果丢了 event 对象?

btn.addEventListener('click',event=>{console.log(event.target);// 一切正常});

如果你写成:

btn.addEventListener('click',()=>{console.log(event);// 卧槽,用的是外层 window.event,IE 时代遗留毛病});

箭头函数没有自己的arguments,也没有event参数,千万别混。


几个骚操作让你写得更稳

统一管理事件监听器,自己建个“伪事件表”做登记注销

classEventBook{#store=newMap();// 伪事件表on(target,type,handler,options){target.addEventListener(type,handler,options);constkey=`${target}-${type}-${handler}`;this.#store.set(key,{target,type,handler});}off(target,type,handler){constkey=`${target}-${type}-${handler}`;if(this.#store.has(key)){const{target:t,type:tp,handler:h}=this.#store.get(key);t.removeEventListener(tp,h);this.#store.delete(key);}}offAll(){this.#store.forEach(({target,type,handler})=>{target.removeEventListener(type,handler);});this.#store.clear();}}// 使用constbook=newEventBook();book.on(btn,'click',handler);// 一键清空book.offAll();

开发阶段加个 console.log 打印当前绑了多少监听,防爆炸

letcounter=0;constoriginal=EventTarget.prototype.addEventListener;EventTarget.prototype.addEventListener=function(type,handler,options){console.log(`[Event] +1${type}on${this}`,++counter);returnoriginal.call(this,type,handler,options);};

上线前记得删掉,不然绩效被扣别找我。

用 AbortController 一键清除多个监听,告别 removeEventListener 地狱

constctrl=newAbortController();const{signal}=ctrl;btn1.addEventListener('click',handler1,{signal});btn2.addEventListener('click',handler2,{signal});window.addEventListener('resize',handler3,{signal});// 一键全清ctrl.abort();

浏览器支持:Chrome 88+、Firefox 86+、Safari 14+。
老项目要兼容 IE?那还是老老实实removeEventListener吧,别把老板逼疯。


下次再遇到“点了没反应”,别只会刷新页面了

刷新页面是前端工程师的“重启试试”,治标不治本
先打开控制台,看看元素选对没;
console.log确认回调进没进;
最后瞄一眼事件表——哦不,是瞄一眼你的代码,是不是重复绑、忘了清、this 飘了、元素还没生出来你就绑

和事件表谈一场坦白局,你会发现:

  • 内存泄漏少了,风扇声轻了;
  • 重复绑定没了,bug 收敛了;
  • 幽灵点击防住了,移动端不抖了;
  • 绩效上去了,女朋友也……还是不在,但至少代码稳了。

唠到这儿,五千字应该有了。如果还不过瘾,去把removeEventListener写 100 遍,感受一下事件表的温柔。

欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。

推荐:DTcode7的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!


专栏系列(点击解锁)学习路线(点击解锁)知识定位
《微信小程序相关博客》持续更新中~结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等
《AIGC相关博客》持续更新中~AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结
《HTML网站开发相关》《前端基础入门三大核心之html相关博客》前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识
《前端基础入门三大核心之JS相关博客》前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。
通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心
《前端基础入门三大核心之CSS相关博客》介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页
《canvas绘图相关博客》Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化
《Vue实战相关博客》持续更新中~详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅
《python相关博客》持续更新中~Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具
《sql数据库相关博客》持续更新中~SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能
《算法系列相关博客》持续更新中~算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维
《IT信息技术相关博客》持续更新中~作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识
《信息化人员基础技能知识相关博客》无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方
《信息化技能面试宝典相关博客》涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面
《前端开发习惯与小技巧相关博客》持续更新中~罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等
《photoshop相关博客》持续更新中~基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结
日常开发&办公&生产【实用工具】分享相关博客》持续更新中~分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具

吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!

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

图解KV Cache:小白也能懂的原理入门

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个KV Cache教学演示网页&#xff0c;包含&#xff1a;1. 可交互的Transformer注意力机制示意图 2. KV Cache动态填充的动画演示 3. 简单的问题回答示例展示Cache作用 4. 对比…

作者头像 李华
网站建设 2026/2/1 14:18:39

Rembg抠图API调用指南:快速集成到你的应用

Rembg抠图API调用指南&#xff1a;快速集成到你的应用 1. 智能万能抠图 - Rembg 在图像处理领域&#xff0c;自动去背景是一项高频且关键的需求&#xff0c;广泛应用于电商商品展示、证件照制作、设计素材提取等场景。传统手动抠图效率低、成本高&#xff0c;而基于深度学习的…

作者头像 李华
网站建设 2026/2/2 15:03:54

SpringBoot+Vue 购物推荐网站平台完整项目源码+SQL脚本+接口文档【Java Web毕设】

摘要 随着电子商务的快速发展&#xff0c;个性化购物推荐系统成为提升用户体验和商家销售转化率的关键技术。传统的电商平台通常依赖用户主动搜索或简单的分类浏览&#xff0c;难以精准满足用户的个性化需求。基于协同过滤和内容推荐的智能算法能够分析用户历史行为数据&#x…

作者头像 李华
网站建设 2026/2/2 18:29:16

招数:最后回归测试

招数:最后回归测试 项目临近结束时&#xff0c;所有人员(开发、管理、测试)都要回归测试所有的Bug。每个人都要帮助团队确保这些Bug的确是被修复了&#xff0c;而且别的更改没有导致功能的"回归"。为便于管理&#xff0c;我们可以考虑新增一个字段&#xff0c;标记某…

作者头像 李华
网站建设 2026/2/2 17:56:16

如何用AI简化SEATA分布式事务配置

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 使用AI生成一个基于Spring Cloud和SEATA的分布式事务配置示例。要求包含&#xff1a;1. SEATA Server的Docker配置&#xff1b;2. Spring Boot项目中SEATA客户端的yml配置&#xf…

作者头像 李华