恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

现代 JavaScript 教程:鼠标移动事件完全指南——mouseover/mouseout、relatedTarget 与 mouseenter/mouseleave 实战解析

  • 首页
  • 资讯中心
  • /
  • 现代 JavaScript 教程:鼠标移动事件完全指南——mouseover/mouseout、relatedTarget 与 mouseenter/mouseleave 实战解析

相关资讯

text-to-cad 实战:从自然语言到 STEP/STL 的几何生成流水线 2026/10/9 4:38:11
客户拜访总是记不全?我用这招把客户需求摸得透透的 2026/10/9 4:38:11
粤语会议听不懂、记不全?这款录音转文字工具,彻底解放你的耳朵和双手 2026/10/9 4:38:11

最新资讯

Claude Opus 5.5 震撼发布:AI 竞争下半场,真正的护城河在哪?
API聚合平台解析2026:技术原理、选型标准、主流平台与接入
MFC工业软件换上4K屏后界面模糊?BCGControlBar Pro如何适配高DPI与多屏
画质旗舰硬碰硬:索尼电视7系二代对比海信E8S,真体验比数字更值钱
基于微信小程序的智能拍卖系统设计与实现复盘
不用编程不用组态,快速实现汇川 EVO系列PLC通过CIP/EIP协议与西门子PLC之间标签方式数据通讯

今日推荐

AI编程智能体实战:从写代码到指挥代码的架构与落地
多模态大模型全栈能力拆解:从数据对齐到弹性推理
大模型Agent开发入门:从工具调用循环到落地避坑指南

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

现代 JavaScript 教程:鼠标移动事件完全指南——mouseover/mouseout、relatedTarget 与 mouseenter/mouseleave 实战解析

发布时间:2026/10/9 4:38:11
现代 JavaScript 教程:鼠标移动事件完全指南——mouseover/mouseout、relatedTarget 与 mouseenter/mouseleave 实战解析 文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载鼠标在元素之间移动时会触发一组高频且容易踩坑的事件mouseover/mouseout、mousemove以及mouseenter/mouseleave。本文以 现代 JavaScript 教程 的「移动鼠标」章节为主体结合仓库内的全部可运行演示.view目录与配套练习源码深入解析这些事件的行为差异、relatedTarget的语义、快速移动导致的元素跳过现象以及如何用mouseover/mouseout实现mouseenter/mouseleave做不到的事件委托。读完本文你将能够准确区分五类鼠标移动事件写出不会因子元素移动而误触发的处理程序并实现单元格级别的悬停高亮与「智能」工具提示。事件 mouseover/mouseout 与 relatedTarget当鼠标指针移到某个元素上时触发mouseover事件鼠标离开该元素时触发mouseout事件。这是最直观的一对移动事件这对事件最特别的地方在于它们拥有relatedTarget属性——它是对target的补充。当鼠标从一个元素离开并进入另一个元素时其中一个元素成为target另一个则成为relatedTarget。对于mouseoverevent.target—— 鼠标当前移入的那个元素event.relatedTarget—— 鼠标来自的那个元素方向为relatedTarget→target。mouseout则正好相反event.target—— 鼠标离开的元素event.relatedTarget—— 鼠标移向的、当前指针位置下的元素方向为target→relatedTarget。仓库中的演示 mouseoverout.view/index.html 将三张笑脸及其眼睛、嘴等器官都设计为独立元素其处理脚本 script.js 把每个事件连同target和relatedTarget一起写入日志区域container.onmouseover container.onmouseout handler; function handler(event) { function str(el) { if (!el) return null; return el.className || el.tagName; } log.value event.type : target str(event.target) , relatedTarget str(event.relatedTarget) \n; if (event.type mouseover) { event.target.style.background pink; } if (event.type mouseout) { event.target.style.background ; } }移动鼠标时即可看到每次mouseover的relatedTarget都指向「上一个元素」每次mouseout的relatedTarget都指向「即将进入的元素」从而可以精确还原鼠标的移动轨迹。注意relatedTarget 可以为 nullrelatedTarget属性可以是null这是正常现象仅表示鼠标并非来自另一个元素而是从窗口之外进入页面或者正在离开窗口。因此在代码中使用event.relatedTarget时必须牢记这种可能性如果直接访问event.relatedTarget.tagName在relatedTarget为null时就会抛出错误。安全的写法是先做空值判断再访问其属性后面的事件委托示例正是这样做的。鼠标快速移动时会跳过元素鼠标移动时触发mousemove事件但这并不意味着每个像素都会产生一个事件。浏览器会持续检查鼠标位置只有检测到位置变化时才触发事件。因此如果访问者非常快地移动鼠标某些 DOM 元素可能被「跳过」假设鼠标从图中的#FROM快速移动到#TO中间的div或其部分子元素可能被跳过mouseout在#FROM上触发后紧接着就在#TO上触发mouseover。这对性能是件好事——页面中可能存在大量中间元素我们并不希望逐一处理每一次移入和离开。但另一方面要记住鼠标指针并不会「访问」所有元素它可能「跳过」一些元素。特别是鼠标指针还可能直接从窗口外跳到页面中间。此时relatedTarget为null因为鼠标「凭空出现」演示 mouseoverout-fast.view/index.html 用嵌套的div idparent与div idchild让你实时体会跳过现象快速扫过时可能只有#child触发事件、只有#parent触发事件甚至两者都不触发把鼠标移入#child后快速向下甩出父级速度足够快时父元素会被完全忽略。一个可以确定的结论有 mouseover 就必有 mouseout快速移动时中间元素可能被忽略但有一件事可以确定如果鼠标指针「正式地」进入了一个元素生成了mouseover事件那么一旦它离开我们一定会得到对应的mouseout事件。这一性质保证了「进入—离开」配对的完整性是后面事件委托逻辑能够正确收尾的前提。移动到子元素时也会触发 mouseoutmouseout的一个重要特性是当鼠标指针从元素移动到其后代时也会触发mouseout。例如下面的 HTML 中鼠标从#parent移到#childdiv idparent div idchild.../div /div如果鼠标正停在#parent上然后更深入地移入#child在#parent上就会得到mouseout这听起来奇怪但很容易解释根据浏览器的逻辑鼠标指针随时可能位于单个元素上——嵌套最深z-index 最大的那个元素。因此一旦指针转到另一个元素哪怕是后代它就离开了前一个元素。还有一个重要的处理细节后代的mouseover事件会冒泡所以如果#parent上注册了mouseover处理程序它同样会被触发演示 mouseoverout-child.view/index.html 在#parent上挂载mouseover/out处理程序脚本见 script.js。把鼠标从#parent移到#child会看到#parent上依次出现两个事件mouseout [target: parent]—— 离开 parentmouseover [target: child]—— 进入 child事件冒泡上来的。用代码表示就是parent.onmouseout function(event) { /* event.target: parent element */ }; parent.onmouseover function(event) { /* event.target: child element (bubbled) */ };如果不检查处理程序中的event.target看起来就像鼠标先离开了#parent又立刻回来了。但事实并非如此——鼠标指针始终位于父元素内部只是更深入地移入了子元素而已。假如我们在parent.onmouseout里运行某个行为例如一段退出动画当鼠标深入#parent内部时我们并不希望它被触发。解决办法有两种在处理程序中检查relatedTarget若鼠标指针仍在元素内部即relatedTarget是该元素或其祖先就忽略这类事件换用mouseenter和mouseleave事件它们天生没有这个问题。事件 mouseenter 与 mouseleavemouseenter/mouseleave与mouseover/mouseout类似都在鼠标指针进入/离开元素时触发但有两个重要区别元素内部与后代之间的转换不会触发事件进入子元素不会触发mouseleave从子元素返回不会触发mouseentermouseenter/mouseleave不会冒泡。这两个事件非常简单鼠标指针进入元素时触发mouseenter指针在元素或其后代中的确切位置无关紧要只有鼠标指针真正离开该元素时才触发mouseleave。演示 mouseleave.view/index.html 与前面的mouseoverout-child结构相同但最外层元素改用onmouseentermouselog(event)与onmouseleavemouselog(event)内联监听脚本见 script.js。可以看到鼠标移入#parent、再进入#child、返回#parent全程只有mouseenter与mouseleave各触发一次后代之间的移动被完全忽略。正是由于「内部转换不触发 不冒泡」这两个特性mouseenter/mouseleave非常适合处理「整块元素」级别的进入/离开而不会像mouseover/mouseout那样在内部元素间穿梭时产生一连串噪声事件。事件委托用 mouseover/mouseout 模拟整块元素的进入/离开mouseenter/leave虽然简单易用但不冒泡因此无法用于事件委托。假设要为表格的单元格处理鼠标进入/离开而表格有数百个单元格。常规思路是在table上设置一个处理程序统一处理但mouseenter/leave不冒泡——如果类似事件发生在td上只有td自身的处理程序能捕获到table上的mouseenter/leave处理程序只在鼠标进入/离开整个表格时触发无法获得表格内部移动的任何信息。所以这种情况下只能使用会冒泡的mouseover/mouseout。先从最简单的高亮处理程序开始见 mouseenter-mouseleave-delegation.view/script.js// 高亮显示鼠标指针下的元素 table.onmouseover function(event) { let target event.target; target.style.background pink; }; table.onmouseout function(event) { let target event.target; target.style.background ; };这个版本会高亮鼠标指针下的每一个元素包括td内部的strong、br等。但我们的需求是处理单元格td之间的移动进入一个单元格并离开它对单元格内部或表格外部的移动不感兴趣。因此需要过滤掉这些额外事件。过滤思路如下在变量中记住当前被高亮显示的td记为currentElemmouseover时——如果仍然停留在当前的td内则忽略该事件mouseout时——如果没有真正离开当前的td则忽略。完整的实现见 mouseenter-mouseleave-delegation-2.view/script.js配套页面 index.html 是一张「八卦图」表格// 现在位于鼠标下方的 td如果有 let currentElem null; table.onmouseover function(event) { // 在进入一个新的元素前鼠标总是会先离开前一个元素 // 如果设置了 currentElem那么我们就没有鼠标所悬停在的前一个 td // 忽略此事件 if (currentElem) return; let target event.target.closest(td); // 我们移动到的不是一个 td —— 忽略 if (!target) return; // 现在移动到了 td 上但处于了我们表格的外部可能因为是嵌套的表格 // 忽略 if (!table.contains(target)) return; // 给力我们进入了一个新的 td currentElem target; onEnter(currentElem); }; table.onmouseout function(event) { // 如果我们现在处于所有 td 的外部则忽略此事件 // 这可能是一个表格内的移动但是在 td 外 // 例如从一个 tr 到另一个 tr if (!currentElem) return; // 我们将要离开这个元素 —— 去哪儿可能是去一个后代 let relatedTarget event.relatedTarget; while (relatedTarget) { // 到父链上并检查 —— 我们是否还在 currentElem 内 // 然后发现这只是一个内部移动 —— 忽略它 if (relatedTarget currentElem) return; relatedTarget relatedTarget.parentNode; } // 我们离开了 td。真的。 onLeave(currentElem); currentElem null; }; // 任何处理进入/离开一个元素的函数 function onEnter(elem) { elem.style.background pink; text.value over - ${elem.tagName}.${elem.className}\n; text.scrollTop 1e6; } function onLeave(elem) { elem.style.background ; text.value out - ${elem.tagName}.${elem.className}\n; text.scrollTop 1e6; }这个方案的关键点有两处依赖事件委托table上只有一个mouseover/mouseout处理程序即可覆盖表格中任意td的进入/离开。这依赖mouseover/out的冒泡特性mouseenter/leave无法做到过滤内部移动mouseout时沿relatedTarget的父链向上查找只要发现仍位于currentElem内部包括relatedTarget本身等于currentElem就判定为「内部移动」并忽略。此外event.target.closest(td)与table.contains(target)两道关卡分别过滤掉「非td目标」与「表格外部的td如嵌套表格」两种情况。实际运行中无论鼠标快移还是慢移只有td会被作为一个整体高亮单元格内部元素之间的移动不会导致高亮闪烁——这正是mouseenter/leave的语义却通过可冒泡的mouseover/out实现了委托化。延伸实战用速度测量实现「智能」工具提示掌握了上述事件特性后可以挑战仓库配套练习 2-hoverintent/task.md编写一个仅在访问者把鼠标移到元素上并停下来时才显示工具提示的函数「移至」而非「移过」。技术思路是测量元素上的鼠标移动速度速度慢则视为停留并显示提示速度快则忽略。练习要求实现通用对象new HoverIntent(options)其options为elem—— 要跟踪的元素over—— 鼠标在元素上移动速度很慢或停住时要调用的函数out—— 鼠标离开元素时调用的函数前提是over已被调用过。用法示例// 一个简单的工具提示 let tooltip document.createElement(div); tooltip.className tooltip; tooltip.innerHTML Tooltip; // 该对象将跟踪鼠标并调用 over/out new HoverIntent({ elem, over() { tooltip.style.left elem.getBoundingClientRect().left px; tooltip.style.top elem.getBoundingClientRect().bottom 5 px; document.body.append(tooltip); }, out() { tooltip.remove(); } });参考实现 2-hoverintent/solution.view/hoverIntent.js 直接体现了本文章节的核心原理其中算法要点如下在元素上挂mouseover/out处理程序也可用mouseenter/leave但通用性较差无法配合事件委托鼠标进入元素后在mousemove上开始测量速度速度慢则运行over鼠标真正移出元素且over已执行时运行out。速度测量的关键细节是JavaScript 中不存在getCurrentMouseCoordinates()这样的函数无法直接获取「鼠标当前坐标」唯一途径是通过mousemove事件记录坐标。因此实现中用onMouseMove持续记录最近一次坐标与时间再通过setInterval每interval毫秒默认100ms见构造函数默认值调用一次trackSpeed()计算前后坐标的欧氏距离除以时间差得到速度speed Math.sqrt( Math.pow(this.prevX - this.lastX, 2) Math.pow(this.prevY - this.lastY, 2) ) / (this.lastTime - this.prevTime); if (speed this.sensitivity) { // 默认 sensitivity 0.1即 0.1px/ms clearInterval(this.checkSpeedInterval); this.isHover true; this.over.call(this.elem); } else { // 速度较快把当前坐标作为新的「上一次坐标」继续测量 this.prevX this.lastX; this.prevY this.lastY; this.prevTime this.lastTime; }当鼠标指针移动到子元素时mouseout的relatedTarget仍在elem内部通过elem.contains(event.relatedTarget)判断实现会继续保持测量状态而不触发out这与上文「移动到子元素时 mouseout」一节的结论完全一致。最终效果是快速扫过「时钟」元素时什么都不会发生缓慢移动或停住时才出现工具提示且在元素内部移动时提示不会「闪烁」。该练习还附带 test.js 测试通过dispatchEvent合成鼠标事件来验证工具提示行为是否正确。同目录下另一道练习 1-behavior-nested-tooltip/task.md含 solution.view/index.html则要求处理嵌套元素的工具提示行为同样是检验relatedTarget判断与内部移动过滤的典型场景。总结本文系统梳理了mouseover、mouseout、mousemove、mouseenter和mouseleave五类鼠标移动事件需要重点记住以下几点快速移动会跳过中间元素浏览器按位置变化触发mousemove快速移动时中间元素可能被略过但一旦正式触发了mouseover离开时必有mouseoutrelatedTarget是target的补充mouseover/out与mouseenter/leave都携带这一属性标识鼠标来自/去往的元素它可能为null来自窗口外或离开窗口访问其属性前务必判空移动到子元素也触发mouseover/out浏览器假定鼠标一次只位于一个元素上——嵌套最深的那一个。从父元素进入子元素时父元素会先收到mouseout子元素的mouseover又会冒泡上来因此处理程序必须检查event.target或沿relatedTarget父链判断才能过滤掉「内部移动」mouseenter/leave与mouseover/out的关键差异mouseenter/leave在元素内部与后代之间转换时不触发、且不冒泡。前者让它们天然免于内部噪声后者使它们无法用于事件委托需要事件委托时选择mouseover/out通过event.target.closest()定位目标元素、用relatedTarget父链过滤内部移动即可在单个表格处理程序上实现「整块td的进入/离开」语义速度测量可实现悬停判定由于无法直接读取鼠标当前坐标需要通过mousemove记录坐标并以固定间隔如 100ms比较位移速度低于阈值如 0.1px/ms即视为停留——这正是HoverIntent智能工具提示的实现原理。如需动手验证可直接打开 2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave 目录下各个.view演示页面结合本文逐步观察事件日志与高亮行为。赞分享文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载相关推荐JavaScript 鼠标移动事件完全指南mouseover/mouseout 与 mouseenter/mouseleave 的机制、陷阱与事件委托JavaScript 鼠标移动事件完全指南mouseover/mouseout 与 mouseenter/mouseleave 的机制、陷阱与事件委托 鼠标在文档/教程前端Brython 鼠标事件完全指南从 mouseenter 到 DOMEvent 属性详解Brython 鼠标事件完全指南从 mouseenter 到 DOMEvent 属性详解 导读 本文以 BrythonBrowser Python运行在浏编程语言语言运行时编译器前端Modern JavaScript Tutorial 实战解析用 focus 与 keydown 事件实现“键盘驱动的鼠标”移动组件Modern JavaScript Tutorial 实战解析用 focus 与 keydown 事件实现“键盘驱动的鼠标”移动组件 本篇技术指南围绕 Mod文档/教程前端上一篇探索创新语音识别IMS Toucan - 你的智能语音解决方案下一篇all-in-rag 食谱数据解析雪克杯法柠檬水的标准化配方与 RAG 分块实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号