javascript中mouseenter与mouseover的异同
发布时间 - 2026-01-11 01:44:45 点击率:次不知道大家在面试或者工作过程中有没有被 mouseover 和 mouseenter (对应的是 mouseout 和 mouseleave )事件所困扰。自己之前在面试的时候就有被问到诸如mouseover和mouseenter事件的异同之类的问题?当时没有答出来,一直也对这两个事件有点模糊不清,趁着最近正在读 zepto源码 ,准备写一篇这方面的文章,如果有错误,请大家指正。

要说清楚mouseenter与mouseover有什么不同,也许可以从两方面去讲。
是否支持冒泡 事件的触发时机
先来看一张图,对这两个事件有一个简单直观的感受。
mouseenter | onmouseenter event
The event fires only if the mouse pointer is outside the boundaries of the object and the user moves the mouse pointer inside the boundaries of the object. If the mouse pointer is currently inside the boundaries of the object, for the event to fire, the user must move the mouse pointer outside the boundaries of the object and then back inside the boundaries of the object.
大概意思是说:当鼠标从元素的边界之外移入元素的边界之内时,事件被触发。而当鼠标本身在元素边界内时,要触发该事件,必须先将鼠标移出元素边界外,再次移入才能触发。(英语比较渣:no_mouth:,凑合看哈)
Unlike the onmouseover event, the onmouseenter event does not bubble.
大概意思是:和mouseover不同的是,mouseenter不支持事件冒泡 (英语比较渣:no_mouth:,凑合看哈)
由于mouseenter不支持事件冒泡,导致在一个元素的子元素上进入或离开的时候会触发其mouseover和mouseout事件,但是却不会触发mouseenter和mouseleave事件
我们用一张动图来看看他们的区别(或者点击该链接体验)。
我们给左右两边的ul分别添加了 mouseover 和 mouseenter 事件,当鼠标进入左右两边的ul时, mouseover 和 mouseenter 事件都触发了,但是当移入各自的子元素li的时候,触发了左边ul上的mouseover事件,然而右边ul的mouseenter事件没有被触发。
造成以上现象本质上是 mouseenter 事件不支持冒泡所致。
如何模拟mouseenter事件。
可见mouseover事件因其具有冒泡的性质,在子元素内移动的时候,频繁被触发,如果我们不希望如此,可以使用mouseenter事件代替之,但是早期只有ie浏览器支持该事件,虽然现在大多数高级浏览器都支持了mouseenter事件,但是难免会有些兼容问题,所以如果可以自己手动模拟,那就太好了。
关键因素: relatedTarget 要想手动模拟mouseenter事件,需要对mouseover事件触发时的事件对象event属性relatedTarget了解。
relatedTarget事件属性返回与事件的目标节点相关的节点。 对于mouseover事件来说,该属性是鼠标指针移到目标节点上时所离开的那个节点。 对于mouseout事件来说,该属性是离开目标时,鼠标指针进入的节点。 对于其他类型的事件来说,这个属性没有用。
重新回顾一下文章最初的那张图,根据上面的解释,对于ul上添加的mouseover事件来说,relatedTarget只可能是
ul的父元素wrap(移入ul时,此时也是触发mouseenter事件的时候, 其实不一定,后面会说明 ), 或者ul元素本身(在其子元素上移出时), 又或者是子元素本身(直接从子元素A移动到子元素B)。
根据上面的描述,我们可以对relatedTarget的值进行判断:如果值不是目标元素,也不是目标元素的子元素,就说明鼠标已移入目标元素而不是在元素内部移动。
条件1: 不是目标元素 很好判断 e.relatedTarget !== target(目标元素)
条件2:不是目标元素的子元素,这个应该怎么判断呢?
ele.contains
这里需要介绍一个新的api node.contains(otherNode) , 表示传入的节点是否为该节点的后代节点, 如果 otherNode 是 node 的后代节点或是 node 节点本身.则返回true , 否则返回 false
用法案例
<ul class="list"> <li class="item">1</li> <li>2</li> </ul> <div class="test"></div>
let $list = document.querySelector('.list')
let $item = document.querySelector('.item')
let $test = document.querySelector('.test')
$list.contains($item) // true
$list.contains($test) // false
$list.contains($list) // true
那么利用contains这个api我们便可以很方便的验证条件2,接下来我们封装一个 contains(parent, node) 函数,专门用来判断 node 是不是 parent 的子节点
let contains = function (parent, node) {
return parent !== node && parent.contains(node)
}
用我们封装过后的 contains 函数再去试试上面的例子
contains($list, $item) // true contains($list, $test) // false contains($list, $list) // false (主要区别在这里)
这个方法很方便地帮助我们解决了模拟mouseenter事件中的条件2,但是悲催的 ode.contains(otherNode) ,具有浏览器兼容性,在一些低级浏览器中是不支持的,为了做到兼容我们再来改写一下contains方法
let contains = docEle.contains ? function (parent, node) {
return parent !== node && parent.contains(node)
} : function (parent, node) {
let result = parent !== node
if (!result) { // 排除parent与node传入相同的节点
return result
}
if (result) {
while (node && (node = node.parentNode)) {
if (parent === node) {
return true
}
}
}
return false
}
说了这么多,我们来看看用 mouseover 事件模拟 mouseenter 的最终代码
// callback表示如果执行mouseenter事件时传入的回调函数
let emulateEnterOrLeave = function (callback) {
return function (e) {
let relatedTarget = e.relatedTarget
if (relatedTarget !== this && !contains(this, relatedTarget)) {
callback.apply(this, arguments)
}
}
}
详细代码点击
代码示例点击
好了,我们已经通过mouseove事件完整的模拟了mouseenter事件,但是反过头来看看
对于ul上添加的mouseover事件来说,relatedTarget只可能是
ul的父元素wrap(移入ul时,此时也是触发mouseenter事件的时候, 其实不一定,后面会说明 ), 或者ul元素本身(在其子元素上移出时), 又或者是子元素本身(直接从子元素A移动到子元素B)。
我们通过排查2和3,最后只留下1,也就是mouseenter与mouseover事件一起触发的时机。既然这样我们为什么不像这样判断呢?
target.addEventListener('mouseover', function (e) {
if (e.relatedTarget === this.parentNode) {
// 执行mouseenter的回调要做的事情
}
}, false)
这样不是更加简单吗?,何必要折腾通过排查2和3来做?
原因是,target的父元素有一定的占位空间的时后,我们这样写是没有太大问题的,但是反之,这个时候 e.relatedTarget 就可能是target元素的父元素,又祖先元素中的某一个。我们无法准确判断e.relatedTarget到底是哪个元素。所以通过排除2和3应该是个更好的选择。
用mouseout模拟mouseleave事件
当mouseout被激活时,relatedTarget表示鼠标离开目标元素时,进入了哪个元素,我们同样可以对relatedTarget的值进行判断:如果值不是目标元素,也不是目标元素的子元素,就说明鼠标已移出目标元素
我们同样可以用上面封装的函数完成
// callback表示如果执行mouseenter事件时传入的回调函数
let emulateEnterOrLeave = function (callback) {
return function (e) {
let relatedTarget = e.relatedTarget
if (relatedTarget !== this && !contains(this, relatedTarget)) {
callback.apply(this, arguments)
}
}
}
结尾
文中也许有些观点不够严谨,欢迎大家拍砖。
# mouseover
# mouseenter
# js
# jquery
# 浅谈JQ中mouseover和mouseenter的区别
# 关于事件mouseover
# mouseout
# mouseleave的区别
# 快速移动鼠标触发问题及解决方法(ECharts外部调用保存为图片操作及工作流接线mouseenter
# Jquery利用mouseenter和mouseleave实现鼠标经过弹出层且可以点击
# 为非IE浏览器添加mouseenter
# mouseleave事件的实现代码
# 跨浏览器的 mouseenter mouseleave 以及 compareDocumentPosi
# javascript 兼容FF的onmouseenter和onmouseleave的代码
# 鼠标
# 来看看
# 不支持
# 的是
# 移出
# 回调
# 好了
# 这两个
# 或者是
# 英语
# 很方便
# 其子
# 鼠标指针
# 当鼠标
# 他们的
# 是个
# 是在
# 在这里
# 很好
# 从子
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Bootstrap整体框架之JavaScript插件架构
*服务器网站为何频现安全漏洞?
教学论文网站制作软件有哪些,写论文用什么软件
?
Linux虚拟化技术教程_KVMQEMU虚拟机安装与调优
制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?
高端云建站费用究竟需要多少预算?
标题:Vue + Vuex 项目中正确使用 JWT 进行身份认证的实践指南
如何快速启动建站代理加盟业务?
网页制作模板网站推荐,网页设计海报之类的素材哪里好?
关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)
如何在浏览器中启用Flash_2025年继续使用Flash Player的方法【过时】
在centOS 7安装mysql 5.7的详细教程
Laravel怎么调用外部API_Laravel Http Client客户端使用
如何获取免费开源的自助建站系统源码?
Laravel如何与Inertia.js和Vue/React构建现代单页应用
浅述节点的创建及常见功能的实现
HTML透明颜色代码怎么让图片透明_给img元素加透明色的技巧【方法】
Windows10如何删除恢复分区_Win10 Diskpart命令强制删除分区
Zeus浏览器网页版官网入口 宙斯浏览器官网在线通道
Laravel Eloquent关联是什么_Laravel模型一对一与一对多关系精讲
Laravel怎么写单元测试_PHPUnit在Laravel项目中的基础测试入门
如何在万网ECS上快速搭建专属网站?
韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南
北京的网站制作公司有哪些,哪个视频网站最好?
rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted
Laravel如何使用Service Provider注册服务_Laravel服务提供者配置与加载
Laravel怎么实现验证码(Captcha)功能
html文件怎么打开证书错误_https协议的html打开提示不安全【指南】
详解vue.js组件化开发实践
中国移动官方网站首页入口 中国移动官网网页登录
网站广告牌制作方法,街上的广告牌,横幅,用PS还是其他软件做的?
JavaScript如何实现错误处理_try...catch如何捕获异常?
如何在阿里云通过域名搭建网站?
香港服务器网站生成指南:免费资源整合与高速稳定配置方案
开心动漫网站制作软件下载,十分开心动画为何停播?
如何快速搭建高效服务器建站系统?
北京专业网站制作设计师招聘,北京白云观官方网站?
Laravel如何获取当前用户信息_Laravel Auth门面获取用户ID
深圳网站制作培训,深圳哪些招聘网站比较好?
html5如何实现懒加载图片_ intersectionobserver api用法【教程】
Laravel的契約(Contracts)是什么_深入理解Laravel Contracts与依赖倒置
Python文本处理实践_日志清洗解析【指导】
如何在万网主机上快速搭建网站?
laravel怎么配置和使用PHP-FPM来优化性能_laravel PHP-FPM配置与性能优化方法
WordPress 子目录安装中正确处理脚本路径的完整指南
如何选择可靠的免备案建站服务器?
如何在橙子建站中快速调整背景颜色?
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
5种Android数据存储方式汇总
潮流网站制作头像软件下载,适合母子的网名有哪些?
下一篇:Linux Perf Tools
下一篇:Linux Perf Tools

