宝哥软件园

javascript中鼠标输入和鼠标悬停的异同

编辑:宝哥软件园 来源:互联网 时间:2021-09-04

不知道你在面试或者工作中有没有被鼠标悬停和鼠标进入(对应鼠标退出和鼠标离开)所困扰。之前面试的时候有人问我鼠标悬停和鼠标进入事件的异同?当时没有回答,对这两个事件一直有点模糊。我打算在最近阅读zepto源代码时写一篇关于这方面的文章。如果有任何错误,请纠正我。

鼠标进入和鼠标悬停有什么异同?

为了澄清鼠标进入和鼠标悬停的区别,我们可以从两个方面来谈。

你支持冒泡事件的触发时间吗

首先,看一张图片,简单直观地感受这两个事件。

看官方网站对mouseenter的解释

mouseenter | onmouseenter事件

只有当鼠标指针位于对象边界之外,并且用户将鼠标指针移动到对象边界之内时,事件才会触发。如果鼠标指针当前位于对象的边界内,则要触发事件,用户必须将鼠标指针移出对象的边界,然后再移回对象的边界内。

当鼠标从元素边界之外移动到元素边界之内时,事件被触发。当鼠标本身位于元素边界内时,要触发此事件,必须先将鼠标移出元素边界,然后再将其移入。(英语比较渣:no_mouth:凑合用吧)

与onmouseover事件不同,onmouseenter事件不会冒泡。

与mouseover不同,mouseenter不支持事件冒泡(英文比较渣:no _ mouth:凑合用吧)

因为mouseenter不支持事件冒泡,所以当进入或离开元素的子元素时,将触发mouseover和mouseout事件,但是不会触发mouseenter和mouseleave事件

让我们用一张动图来看看它们之间的区别(或者点击链接来体验一下)。

我们分别在左侧和右侧uls中添加了mouseover和mouseenter事件。当鼠标进入左右uls时,mouseover和mouseenter事件都被触发,但是当它们移动到各自的子元素li时,左边UL的mouseover事件被触发,但是右边UL的mouseenter事件没有被触发。

上述现象本质上是由于mouseenter事件不支持冒泡造成的。

如何模拟mouseenter事件?

可以看到,由于mouseover事件的冒泡属性,当它在子元素中移动时,它会被频繁触发。如果我们不想这样,我们可以使用mouseenter事件来代替。然而,在早期,只有ie浏览器支持这个事件。虽然现在大多数高级浏览器都支持mouseenter事件,但是难免会出现一些兼容性问题,所以如果我们能手动模拟一下就好了。

Key Factor : relatedTarget如果要手动模拟mouseenter事件,需要知道触发鼠标悬停事件时事件对象的事件属性相关目标。

relatedTarget事件属性返回与事件的目标节点相关的节点。对于鼠标悬停事件,此属性是鼠标指针在目标节点上移动时离开的节点。对于mouseout事件,此属性是鼠标指针离开目标时进入的节点。此属性对于其他类型的事件没有用。

回看文章原图,根据上面的解释,对于ul上添加的鼠标悬停事件,relatedTarget只能是

Ul的父元素换行(移入ul时,也是触发mouseenter事件的时间,后面不一定会解释),或者ul元素本身(当其子元素移出时),或者子元素本身(直接从子元素a移动到子元素b)。

相关目标

根据上面的描述,我们可以判断relatedTarget的值:如果该值不是目标元素或目标元素的子元素,则意味着鼠标已经移动到目标元素中,而不是移动到元素内部。

条件1:如果不是目标元素,很容易判断e.relatedTarget!==目标(目标元素)

条件2:它不是目标元素的子元素。这应该如何判断?

包含

这里,我们需要引入一个新的api node.contains(otherNode),它指示传入的节点是否是该节点的后代节点。如果otherNode是该节点或节点本身的后代节点,则返回true,否则返回false

使用案例

ul class=' list ' Li class=' item ' 1/Li Li 2/Li/uldiv class=' test '/div let $ list=document . queryselector('。list ')让$item=document.querySelector('。item ')让$test=document.querySelector('。test ')$ list . contains($ item)//True $ list . contains($ test)//True $ list。contains ($ list)//true,那么使用api contains,我们可以很容易地验证条件2。接下来,我们封装一个contains(parent,node)函数,专门用来判断node是否为parent的子节点。

let包含=函数(父,节点){ return parent!==node parent.contains(node)}使用我们封装的contains函数再次尝试上面的示例

Contains ($ list,$ item)//True contains ($ list,$ test)//False contains ($ list,$ list)//False(主要区别在这里)。该方法可以方便地解决模拟鼠标进入事件的条件2。但是,sad ode.contains(otherNode)具有浏览器兼容性,在某些低级浏览器中不受支持。为了兼容,我们将再次重写contains方法

let contains=docEle.contains?函数(父,节点){返回父!==node parent . contains(node)} :函数(parent,node) { let result=parent!==节点if(!Result) {//排除父节点和节点传入同一个节点返回结果} if(result){ while(node(node=node。parent node)){ if(parent==node){ Return true } } } Return false }说到这里,让我们看一下用mouseover事件模拟mouseenter的最终代码。

//callback的意思是,如果在执行mouseenter事件时传入了回调函数,那么让simulate enter alive=function(callback){ return function(e){ let related target=e . related target if(related target!==这个!包含(此,相关目标)){回调。应用(此,参数)}}单击详细代码

代码示例点击

好了,我们已经通过mouseove事件完全模拟了mouseenter事件,但是回头看看。

对于ul上添加的鼠标悬停事件,相关目标只能是

Ul的父元素换行(移入ul时,也是触发mouseenter事件的时间,后面不一定会解释),或者ul元素本身(当其子元素移出时),或者子元素本身(直接从子元素a移动到子元素b)。

我们检查了2和3,最后只剩下1,这是mouseenter和mouseover事件一起触发的时间。既然这样,我们为什么不这样判断呢?

瞄准不是更简单吗?addeventlistener ('mouseover ',函数(e) {if (e. related target===this。parent node){//执行mouse enter } } }的回调时怎么办,false)?为什么要费心通过筛选2和3来做呢?

原因是当Target的父元素有一定的空间时,我们这样写并不是什么大问题,而恰恰相反,此时的e.relatedTarget可能是target元素的父元素,也是它的祖先之一。我们无法准确判断e.relatedTarget是哪个元素。所以排除2和3应该是更好的选择。

使用mouseout模拟mouseleave事件

当mouseout被激活时,relatedTarget指示当鼠标离开目标元素时输入了哪个元素。我们还可以判断relatedTarget的值:如果该值不是目标元素或其子元素,则表示鼠标已经移出了目标元素。

我们也可以用上面封装的函数来实现

//callback的意思是,如果在执行mouseenter事件时传入了回调函数,那么让simulate enter alive=function(callback){ return function(e){ let related target=e . related target if(related target!==这个!包含(此,相关目标)){回调。应用(这个,参数)}}}

也许本文的一些观点不够严谨。欢迎拍砖。

更多资讯
游戏推荐
更多+