mouseenter与mouseover为何这般纠缠不清?

news/2024/4/28 20:32:30/文章来源:https://blog.csdn.net/weixin_34357267/article/details/89058886

前言

原文地址

项目地址

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

<!--more-->

mouseenter与mouseover的异同?

要说清楚mouseenter与mouseover有什么不同,也许可以从两方面去讲。

  1. 是否支持冒泡

  2. 事件的触发时机

先来看一张图,对这两个事件有一个简单直观的感受。

再看看官网对mouseenter的解释

mouseenter | onmouseenter event.aspx)

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.

大概意思是说:当鼠标从元素的边界之外移入元素的边界之内时,事件被触发。而当鼠标本身在元素边界内时,要触发该事件,必须先将鼠标移出元素边界外,再次移入才能触发。(英语比较渣?,凑合看哈)

Unlike the onmouseover event, the onmouseenter event does not bubble.

大概意思是:和mouseover不同的是,mouseenter不支持事件冒泡 (英语比较渣?,凑合看哈)

由于mouseenter不支持事件冒泡,导致在一个元素的子元素上进入或离开的时候会触发其mouseover和mouseout事件,但是却不会触发mouseenter和mouseleave事件

我们用一张动图来看看他们的区别(或者点击该链接体验)。

我们给左右两边的ul分别添加了mouseovermouseenter事件,当鼠标进入左右两边的ul时,mouseovermouseenter事件都触发了,但是当移入各自的子元素li的时候,触发了左边ul上的mouseover事件,然而右边ul的mouseenter事件没有被触发。

造成以上现象本质上是mouseenter事件不支持冒泡所致。

如何模拟mouseenter事件。

可见mouseover事件因其具有冒泡的性质,在子元素内移动的时候,频繁被触发,如果我们不希望如此,可以使用mouseenter事件代替之,但是早期只有ie浏览器支持该事件,虽然现在大多数高级浏览器都支持了mouseenter事件,但是难免会有些兼容问题,所以如果可以自己手动模拟,那就太好了。

关键因素: relatedTarget 要想手动模拟mouseenter事件,需要对mouseover事件触发时的事件对象event属性relatedTarget了解。

  1. relatedTarget事件属性返回与事件的目标节点相关的节点。

  2. 对于mouseover事件来说,该属性是鼠标指针移到目标节点上时所离开的那个节点。

  3. 对于mouseout事件来说,该属性是离开目标时,鼠标指针进入的节点。

  4. 对于其他类型的事件来说,这个属性没有用。

重新回顾一下文章最初的那张图,根据上面的解释,对于ul上添加的mouseover事件来说,relatedTarget只可能是

  1. ul的父元素wrap(移入ul时,此时也是触发mouseenter事件的时候, 其实不一定,后面会说明),

  2. 或者ul元素本身(在其子元素上移出时),

  3. 又或者是子元素本身(直接从子元素A移动到子元素B)。

relatedTarget

根据上面的描述,我们可以对relatedTarget的值进行判断:如果值不是目标元素,也不是目标元素的子元素,就说明鼠标已移入目标元素而不是在元素内部移动。

条件1: 不是目标元素很好判断e.relatedTarget !== target(目标元素)

条件2:不是目标元素的子元素,这个应该怎么判断呢?

ele.contains

这里需要介绍一个新的api [node.contains(otherNode)

](https://developer.mozilla.org... 表示传入的节点是否为该节点的后代节点, 如果 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 !== nodeif (!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.relatedTargetif (relatedTarget !== this && !contains(this, relatedTarget)) {callback.apply(this, arguments)}}
}

模拟mouseenter与原生mouseenter事件效果对比

html

<div class="wrap">wrap, mouseenter<ul class="mouseenter list">count: <span class="count"></span><li>1</li><li>2</li><li>3</li></ul>
</div><div class="wrap">wrap, emulate mouseenter,用mouseover模拟实现mouseenter<ul class="emulate-mouseenter list">count: <span class="count"></span><li>1</li><li>2</li><li>3</li></ul>
</div>

css

.wrap{width: 50%;box-sizing: border-box;float: left;
}.wrap, .list{border: solid 1px green;padding: 30px;margin: 30px 0;
}.list{border: solid 1px red;
}.list li{border: solid 1px blue;padding: 10px;margin: 10px;
}.count{color: red;
}

javascript

let $mouseenter = document.querySelector('.mouseenter')
let $emulateMouseenter = document.querySelector('.emulate-mouseenter')
let $enterCount = document.querySelector('.mouseenter .count')
let $emulateMouseenterCounter = document.querySelector('.emulate-mouseenter .count')let addCount = function (ele, start) {return function () {ele.innerHTML = ++start}
}let docEle = document.documentElementlet contains = docEle.contains ? function (parent, node) {return parent !== node && parent.contains(node)} : function (parent, node) {let result = parent !== nodeif (!result) {return result}if (result) {while (node && (node = node.parentNode)) {if (parent === node) {return true}}}return false
}let emulateMouseenterCallback = addCount($emulateMouseenterCounter, 0)let emulateEnterOrLeave = function (callback) {return function (e) {let relatedTarget = e.relatedTargetif (relatedTarget !== this && !contains(this, relatedTarget)) {callback.apply(this, arguments)}}
}$mouseenter.addEventListener('mouseenter', addCount($enterCount, 0), false)
$emulateMouseenter.addEventListener('mouseover', emulateEnterOrLeave(emulateMouseenterCallback), false)  

效果预览

详细代码点击

代码示例点击

好了,我们已经通过mouseove事件完整的模拟了mouseenter事件,但是反过头来看看

对于ul上添加的mouseover事件来说,relatedTarget只可能是

  1. ul的父元素wrap(移入ul时,此时也是触发mouseenter事件的时候, 其实不一定,后面会说明),

  2. 或者ul元素本身(在其子元素上移出时),

  3. 又或者是子元素本身(直接从子元素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.relatedTargetif (relatedTarget !== this && !contains(this, relatedTarget)) {callback.apply(this, arguments)}}
}

详细代码点击

代码示例点击

结尾

文中也许有些观点不够严谨,欢迎大家拍砖。

原文地址

项目地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_840781.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【高海东】通过避免下列 10 个常见 ASP.NET 缺陷使网站平稳运行

LoadControl 和输出缓存会话和输出缓存Forms 身份验证票证生存期视图状态&#xff1a;无声的性能杀手SQL Server 会话状态&#xff1a;另一个性能杀手未缓存的角色配置文件属性序列化线程池饱和模拟和 ACL 授权不要完全信赖它 — 请设置数据库的配置文件&#xff01;ASP.NET 成…

电商网站秒杀倒计时实现

电商网站秒杀倒计时实现 CSS代码实现 <style> #container{ width: 190px; height: 275px; background: #e83632; color: #fff; position: relative; } #container .sk_title{ position: absolute; left:0; top: 42px; width: 100%; text-align: center; font-size: 34px;…

夺命雷公狗—玩转SEO---3---什么是权重

三、权重 爱站和站长工具都可以进行查询&#xff0c;权重和词库是有关系的&#xff0c;词库回关系到预计流量 也就是说网站中的词库越多&#xff0c;预计流量那么也就也多&#xff0c;预计流量越多&#xff0c;流量也就越高&#xff0c;预计流量多少&#xff0c;分别对应权重几…

使用phpStudy部署PHP+IIS+Mysql网站

1 下载phpStudy工具安装文件 2.解压后&#xff0c;一般会在D盘根目录下有个www文件夹&#xff08;如果安装时修改了路径&#xff0c;找到对应路径下的www文件夹&#xff09;&#xff0c;把你整个项目文件夹放到www目录下&#xff0c;如下图中的 3.修改你的项目&#xff0c;使其…

干货 | 云解析DNS之网站监控

云解析是在域名解析的基础上&#xff0c;由京东云团队&#xff0c;结合京东云的优质网络、主机资源研发的高可用、高可靠、功能丰富的权威DNS服务器。云解析拥有简单易用的控制台&#xff0c;方便用户对域名进行操作。采用多集群、多节点部署&#xff0c;拥有百G防护套餐&#…

在IIS上部署PHP网站

部署网站前查看一下系统是否已经安装CGI 1、启动iis服务器&#xff0c;打开IIS服务器 打开IIS服务器&#xff0c;点击网站&#xff0c;右击“添加网站” 2、创建网站 点击“添加网站”后&#xff0c;进入页面填写网站相关内容&#xff0c;如&#xff1a;网站名称、物理路径(…

Apache服务器中怎么配置网站的默认首页default.php

Apache服务器中怎么配置网站的默认首页 在 httpd.conf 文件中找到DirectoryIndex代码段&#xff1b; 将DirectoryIndex代码段修改为&#xff1a;DirectoryIndex default.html default.php index.php index.html样式&#xff0c;便可支持default.php/default.html首页了&#…

如何使用USBWebserver在本机快速建立网站测试环境

记得阿汤在很久以前曾介绍过使用phpnow来架设本机的网站测试环境。因为蛮方便的不过有一个麻烦的地方是经常在测试完后忘记停用apache和mysql服务&#xff0c;启动和关闭完全都是手动的而今天再来介绍一套也是可以带着走的快速架站工具USBWebserv内建了许多网站主机必备的套件&…

【转】大型网站架构演变和知识体系

最近在读《淘宝技术这十年》这本书&#xff0c;作者是子柳&#xff0c;讲述了淘宝网的架构演变过程&#xff0c;感觉挺好的&#xff0c;之前就对网站架构比较感兴趣&#xff0c;所以也会多关注一些架构方向的文章。本篇文章也是看到后感觉很棒&#xff0c;由于转载的并不是真正…

服务端(Win server2012)+IIS管理器配置PHP服务器并部署网站讲解

我打算把IIS搭建一个网站的项目&#xff0c;因项目的后端为php&#xff0c;因此想在IIS配置PHP服务器的环境。说到端口的问题&#xff0c;因为IIS和phpstudy的默认端口都为80&#xff0c;这样两个端口会冲突&#xff0c;如何避免端口冲突呢&#xff1f;如何把IIS搭建一个网站项…

调试php时网站出现502的解决方法

这是第二次遇到该问题了&#xff0c;上次解决后因为没有记录&#xff0c;结果这次遇到后又得重新查找解决方法。记忆力严重减退了啊&#xff0c;还是要保持边学边记录的习惯 网站在正常运行时是没有问题的&#xff0c;但是在使用xdebug进行远程调试时&#xff0c;可能会因为单步…

搭建简易Web GIS网站:使用GeoServer+PostgreSQL+PostGIS+OpenLayers3

搭建简易Web GIS网站&#xff1a;使用GeoServerPostgreSQLPostGISOpenLayers3 Web GIS系列&#xff1a; 搭建简易Web GIS网站&#xff1a;使用GeoServerPostgreSQLPostGISOpenLayers3 使用GeoServerQGIS发布WMTS服务 使用GeoServerOpenLayers发布和调用WMTS、Vector Tile矢量切…

如何查询自己的网站备案号码

原文地址&#xff1a;http://www.5u5.cn/a/zhishiku/xunizhuji/2010/0110/141.html 时间:2010-01-10 10:33来源:无忧技术 作者:Wilber82首先登陆工信部备案网站 http://www.miibeian.gov.cn/ 左侧点击 公共查询 进入查询页面&#xff1a;点击 公共信息查询&#xff1a;右边输入…

php网站乱码,完美解决乱码、一键解决PHP的乱码、php网站乱码矫正神器、PHP乱码修复器

php乱码无非就是编码问题&#xff0c;可以使用phpwamp自带的常用工具一键解决 输入你的php版本文件的所在路径&#xff0c;点击修复&#xff0c;然后重启动环境就可以了

使用IIS部署PHP网站

接到通知&#xff0c;需要将云服务器上的PHP网站部署到自己的Windows服务器上。 反驳无效&#xff0c;动手就干。 0、准备工作 0.Windows Server 2012R2 1.IIS8 2.PHP CGI&#xff08; FastCGI 是一种标准协议&#xff0c;可允许应用程序框架的通用网关接口 (CGI) 可执行文件…

java 自适应响应式 网站 源码 SSM 生成 静态化 手机 平板 PC

前台&#xff1a; 支持四套模版&#xff0c; 可以在后台切换访问&#xff1a;三W点1b23点org 系统介绍&#xff1a;1.网站后台采用主流的 SSM 框架 jsp JSTL&#xff0c;网站后台采用freemaker静态化模版引擎生成html2.因为是生成的html&#xff0c;所以访问速度快&#xff0c;…

视频播放网站CDN内容分发网络实现

视频播放如果只有一台视频服务器&#xff0c;当访问用户过多时&#xff0c;服务器将承受不了负载。所以我们需要在视频服务器下面增加边缘服务器&#xff0c;下面以视频服务器加三台边缘服务器为例。网络环境图&#xff1a;1. 用户可通过PC机或手机访问网站。2. 网站将用户请求…

程序员应该访问的最佳网站中文版

原文链接 :https://github.com/tuteng/Best-websites-a-programmer-should-visit-zh/blob/master/README.md 一些对程序员有用的网站 在学习CS的时候有一些你必须知道的有用的站点来获取通知为了你的技术储备和学习新知识。这里是一个你应该访问的不是非常全面的一些站点的列表…

[SEO]让你的Asp.Net网站自动生成Sitemap——XmlSitemap

首先我要说明&#xff1a;Asp.Net内置的Sitemap与这里讲的Sitemap是完全不同的&#xff0c;Asp.Net中的Sitemap主要用于给用户导航&#xff0c;而这里说的Sitemap是用来给搜索引擎爬虫指路。还是直接来看看官方解释吧&#xff1a;什么是Sitemap&#xff1f;Sitemap 可方便管理员…

c# 模拟网站登陆

我们在写灌水机器人、抓资源机器人和Web网游辅助工具的时候第一步要实现的就是用户登录。那么怎么用C#来模拟一个用户的登录拉要实现用户的登录&#xff0c;那么首先就必须要了解一般网站中是怎么判断用户是否登录的。 HTTP协议是一个无连接的协议&#xff0c;也就是说这次对话…