当鼠标悬停(并离开)元素(或其子元素)时运行操作

发布于 2024-08-09 11:06:08 字数 1681 浏览 3 评论 0原文

想象一下我有以下元素:

+------------------+
|                  |
|    +----------------+
|    |                |
|    |                |
|    +----------------+
|                  |
+------------------+

内部元素具有定位,这意味着它可以在视觉上位于其父元素之外,并且我在父元素上放置了一个事件侦听器,如下所示:

parent.addEventListener('mouseover', function(e) {
  // log mouse is over me
}, false);

如果我将鼠标直接从底部移到父元素上,我会直接得到事件,但由于事件路由,如果我将鼠标从侧面悬停(先击中子项),我也会在子项冒泡时收到来自子项的事件。

如果我继续向左移动,直到离开子元素并进入父空间,我将获得一个直接事件,如果我再次向右移动,我将从子元素获得另一个冒泡事件。

这是设计使然,而且设计绝对没问题,但是,如果我想在鼠标悬停在我上方时执行单个操作(包括我的子项目),我需要做一些额外的工作来阻止我不感兴趣的事件。例如,这可以工作:

var isOver = false;

parent.addEventListener('mouseover', function(e) {
  if (isOver) return;
  isOver = true;
  // log mouse is over me
}, false);

parent.addEventListener('mouseout', function(e) {
  if (e.relatedTarget == this || e.relatedTarget.isDescendantOf(this)) return;
  isOver = false;
  // log mouse is leaving me
}, false);

本质上,代码有一个状态变量来确定鼠标是否“越过了”父级(无论是否通过其子级),以及如果事件在“越过”时再次触发,则这些事件将被忽略。 .我们还捕获鼠标移出事件并询问它..是相关目标(您即将进入的目标)ME吗? (父母),还是我最终的孩子?如果是这样,那么不要做任何事情..否则将“overed”状态设置为 false。

因此该代码实际上可以工作,但假设我无法使用 relatedTarget 属性来确定下一个目标,您将如何执行此行为?

我希望这是有道理的,我感觉事件中没有足够的上下文来在没有 relatedTarget 的情况下执行此操作,但感觉很可能存在关于更改 eventPhase 属性行为的角度(以确定事件是直接事件还是冒泡事件)。

另外,我并不是在寻找“这在 IE 中不起作用,yadda yadda”的答案。我现在只针对 w3c 事件规范浏览器进行工作。


编辑,只是为了澄清,我希望事件发生就好像我有一个看起来像这样的元素(如果该元素实际上如上面的示例所示):

+------------------+
|                  |
|                  +--+
|                     |
|                     |
|                  +--+
|                  |
+------------------+

Imagine I have the following elements:

+------------------+
|                  |
|    +----------------+
|    |                |
|    |                |
|    +----------------+
|                  |
+------------------+

The inner element has positioning that mean it can visually sit outside of its parent, and I put an event listener on the parent like such:

parent.addEventListener('mouseover', function(e) {
  // log mouse is over me
}, false);

If I mouse directly over the parent from the bottom, I get a direct event, but because of event routing if I mouse over from the side (hitting the child first), I'll also receive the event from the child as it bubbles up.

If I keep going left until I'm out of the child element and in the parent space, I'll get a direct event, if I go right again, I'll get another bubbled event from the child element.

This is by design and the design is absolutely fine, however- if I want to do a SINGLE action when the mouse overs me (including my child items), I need to do some extra work to block events I'm not interested in.. for example, this would work:

var isOver = false;

parent.addEventListener('mouseover', function(e) {
  if (isOver) return;
  isOver = true;
  // log mouse is over me
}, false);

parent.addEventListener('mouseout', function(e) {
  if (e.relatedTarget == this || e.relatedTarget.isDescendantOf(this)) return;
  isOver = false;
  // log mouse is leaving me
}, false);

Essentially the code has a state variable to determine if the mouse has 'overed' the parent (either via its child or not), and if the event fires again whilst 'overed' those events are ignored.. we also capture the mouse out event and ask it.. is the relatedTarget (the target you are about to enter) ME? (the parent), or an eventual child of ME? if so, then don't do anything.. otherwise set the 'overed' state to false.

So that code actually works, but imagine I cannot use the relatedTarget property to determine the next target, how would you go about doing this behavior?

I hope that makes sense, I have the feeling theres not enough context in the events to do it without relatedTarget but felt there might well be an angle regarding changing behavior on the eventPhase property (to determine if the event is direct or bubbled).

Also I'm not looking for answers saying 'this won't work in IE, yadda yadda'.. I'm working against w3c event spec browsers only right now.


Edit, just to clarify, I want the events to happen as if I have a single element that looked like this (if the element was actually as above example):

+------------------+
|                  |
|                  +--+
|                     |
|                     |
|                  +--+
|                  |
+------------------+

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

南笙 2024-08-16 11:06:08

我认为你追求的是 IE 的 “mouseenter” 事件(当用户将鼠标指针移动到对象边界内时触发)和 "mouseleave" 事件(当用户将鼠标指针移到对象边界之外时触发):

onmouseover 事件不同,
onmouseenter 事件不会冒泡。在
换句话说,onmouseenter 事件
当用户移动时不会触发
鼠标指针悬停在包含的元素上
通过对象,而 onmouseover
确实火了。

onmouseleave 也是如此。

这是一个示例: http://jsbin.com/uputi (添加 /edit到编辑源代码的网址):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 
<head> 
<title>Sandbox</title> 
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<style type="text/css" media="screen"> 
#outer { width:200px; height:200px; border:1px solid #333; }
#inner { float:right; width:125px; height:80px; margin-right:-80px; border:1px solid #333; }
</style> 

<script> 
window.onload = function() {
  function mouseenter() {
    document.getElementById('dbg').innerHTML = 'mouse entered #outer boundary';
  }

  function mouseleave() {
    document.getElementById('dbg').innerHTML = 'mouse left #outer boundary';
  }

  var outer = document.getElementById('outer');

  if ('onmouseenter' in document.body) {
    // browser supports mouseenter/mouseleave natively (i.e., IE)
    outer.onmouseenter = mouseenter;
    outer.onmouseleave = mouseleave;
  }
  else {
    // simulate mouseenter/mouseleave in other browsers
    function wrap(fn) {
      return function(event) {
        var parent = event.relatedTarget;

        // Check if we're still within the same parent (traverse up parentNodes)
        while (parent && parent != this) {
          parent = parent.parentNode
        }

        if(parent != this) {
          // call the *real* mouseover/mouseout handler
          fn.call(this, event)
        }
      }
    }

    outer.onmouseover = wrap(mouseenter);
    outer.onmouseout = wrap(mouseleave);
  }
}
</script> 
</head> 
<body> 
  <div id="outer"> 
    <div><code>#outer</code></div> 
    <div> </div> 
    <div> </div> 
    <div id="inner"><code>#inner</code></div> 
  </div> 
  <div id="dbg"></div> 
</body> 
</html>

它使用 latedTarget 正如您所做的那样(仅供参考,在标准浏览器中工作正常),但没有状态标志。请参阅 本文了解此处显示的仿真的更多详细信息。
您可以看到在输入 #outer 元素时仅触发一个 mouseenter 事件,以及#inner元素。这就是你所追求的“边界”效应(我认为)。

如果您可以选择在应用程序中包含 jQuery,jQuery 会将这些奇妙的事件带到其他浏览器。 (注意 jQuery 使用了她内部概述的技术,并进行了一些细微的修改)。

Prototype 1.6.1 也有这些事件模拟的。

I think what you are after are IE's "mouseenter" event (fires when the user moves the mouse pointer inside the boundaries of the object) and "mouseleave" event (fires when the user moves the mouse pointer outside the boundaries of the object):

Unlike the onmouseover event, the
onmouseenter event does not bubble. In
other words, the onmouseenter event
does not fire when the user moves the
mouse pointer over elements contained
by the object, whereas onmouseover
does fire.

Ditto for onmouseleave.

Here's an example: http://jsbin.com/uputi (add /edit to the url to edit the source code):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 
<head> 
<title>Sandbox</title> 
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<style type="text/css" media="screen"> 
#outer { width:200px; height:200px; border:1px solid #333; }
#inner { float:right; width:125px; height:80px; margin-right:-80px; border:1px solid #333; }
</style> 

<script> 
window.onload = function() {
  function mouseenter() {
    document.getElementById('dbg').innerHTML = 'mouse entered #outer boundary';
  }

  function mouseleave() {
    document.getElementById('dbg').innerHTML = 'mouse left #outer boundary';
  }

  var outer = document.getElementById('outer');

  if ('onmouseenter' in document.body) {
    // browser supports mouseenter/mouseleave natively (i.e., IE)
    outer.onmouseenter = mouseenter;
    outer.onmouseleave = mouseleave;
  }
  else {
    // simulate mouseenter/mouseleave in other browsers
    function wrap(fn) {
      return function(event) {
        var parent = event.relatedTarget;

        // Check if we're still within the same parent (traverse up parentNodes)
        while (parent && parent != this) {
          parent = parent.parentNode
        }

        if(parent != this) {
          // call the *real* mouseover/mouseout handler
          fn.call(this, event)
        }
      }
    }

    outer.onmouseover = wrap(mouseenter);
    outer.onmouseout = wrap(mouseleave);
  }
}
</script> 
</head> 
<body> 
  <div id="outer"> 
    <div><code>#outer</code></div> 
    <div> </div> 
    <div> </div> 
    <div id="inner"><code>#inner</code></div> 
  </div> 
  <div id="dbg"></div> 
</body> 
</html>

It uses relatedTarget as you do (which works fine in standards browsers FYI), but without the state flag. See this article for more details of the emulation shown here.
You can see only one mouseenter event is fired when entering the #outer element, as well as the #inner element. This is the "boundary" effect you are going after (I think).

If you have the option of including jQuery in your application, jQuery brings these fantastic events to other browsers. (note jQuery uses the technique outlined her internally, with some minor modifications).

Prototype 1.6.1 also has these events simulated.

情绪少女 2024-08-16 11:06:08

我认为你只需要 stopPropagation

I think you simply need to stopPropagation

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文