重叠的兄弟元素之间的条件鼠标事件传递

时间:2014-06-06 13:02:14

标签: javascript html mouseover event-passthrough

是否有标准的Javascript技术或库来管理不相关的重叠HTML元素之间的条件鼠标事件传递?

例如,我在一堆HTML元素前面有一个部分透明的WebGL画布(由Three.js管理)(由Thee.js CSS3渲染器管理,但这不应该是相关的)。那些HTML元素已经注册了mouseover和mouseout事件。我希望在这些元素前面漂浮的3D对象可以阻止鼠标事件。

我已经知道如何使用光线施法器来确定鼠标是否在3D对象上。我不知道的是如何让鼠标事件通过'当3D对象不在其间时,画布到底层HTML元素。

enter image description here

我已经阅读过遍历DOM树的解决方案,直到找到鼠标下方的元素。但这似乎过于复杂和缓慢。我想做的事情,如果可能的话,假装画布暂时没有,以便事件可以自然地通过。

为了不重新发明轮子,如果已经有了这个库,那将会很棒。

3 个答案:

答案 0 :(得分:5)

由于您不想像David Thomas所建议的那样使用指针事件,因此鼠标将始终以具有最高z-index的元素或具有相同z-index的元素作为最后一个兄弟(当相对堆叠时)在其他人之上)。

说完这个,我能想到的唯一方法就是:

  1. 隐藏画布
  2. 读取
  3. 下的元素
  4. 立即展示画布
  5. 这是如此之快,不会产生明显的闪烁。

    如果鼠标下方有元素,则触发该元素鼠标事件。

    Demo here

    $("canvas").mousemove(function (event) {
        if (document.elementFromPoint) {
            var $canvas = $(this);
    
            // hide canvas visibility
            // don't do display:none as we want to maintain canvas layout
            $canvas.css('visibility', 'hidden');
    
            // get the element underneath, if any
            var $under = $(document.elementFromPoint(event.clientX, event.clientY));
    
            // show again the canvas
            $canvas.css('visibility', 'visible');
            if ($under.hasClass('underneath')) { 
               $under.triggerHandler('mouseover');
            } else {
                $("#result").text("mouse is over the canvas");
            }
        }
    })
    

答案 1 :(得分:1)

我找到了一个非常直接的解决方案。既然你在其中一条评论中提供了一个jsfiddle示例,我想我只是稍微调整一下代码并让你看到它。

我正在做的很简单。我检查鼠标是否与div重叠并且函数" changeTheDivBox"当你的Three.js raycaster没有击中任何盒子时被调用。

var divBox = document.getElementById("background");
divBox.addEventListener("mouseover", mouseOver, false);
divBox.addEventListener('mouseout', mouseOut, false);
var mouseOverBox = false; // Are you currently hovering over the "background" div?
function mouseOver () {
    mouseOverBox = true;
}
function mouseOut () {
    mouseOverBox = false;
}
function changeTheDivBox () { // This function is called when your raycaster does not hit any boxes
    if ( mouseOverBox ) {
        divBox.style.backgroundColor = 'blue';
    }
    else {
        divBox.style.backgroundColor = 'green';
    }
}

基本上:

Is the mouse overlapping the <div> ?
    Yes.
    Is the mouse (raycaster) hitting any boxes?
    No.
        Then Change the <div>'s color!

我做的另一件事是使用css属性pointer-events。这可以让你点击divs&#34;进一步返回&#34;通过使任何选定的元素不注册鼠标事件。我添加了pointer-events: none;body禁用所有鼠标事件,然后我将pointer-events: auto;添加到div元素以重新启用它们。所以鼠标现在只检测该div上的事件。非常整洁。

我保留了其余的代码。我个人更喜欢使用循环来不断检查重叠/光线投射是否仍然有效而不是依赖于mouseIn / mouseOutSo,但这是你的例子,所以请玩它:)

UPDATED JSFIDDLE

正如个人偏好。我建议你避免将函数放在HTML中。他们并不总是按预期工作。 this对象一直引用窗口)它们可以使您的代码更加混乱。我个人更喜欢将JavaScript保留在标签内。 而且eventListeners比内联函数调用更强大:)

答案 2 :(得分:0)

您在代码中设置了 #background z-index:-1;的{​​{1}}。这意味着:它位于 body z层的后面,鼠标事件将定位到上层的身体或其他任何内容。

您可以设置 body display:absolute;甚至所有元素,但这可能是一个坏事,以后网站中包含其他代码。首选使用更高的z-index将其他覆盖元素置于绝对位置,它们是: #container #container&gt; canvas

查看路径如何,DOM事件传播:http://www.w3.org/TR/DOM-Level-3-Events/

在捕获阶段,事件首先传播到视图,然后是html和body,然后它将到达鼠标光标下显示的目标。比在起泡阶段,传播采取相反的方式回到视图。

渲染的场景对象不在此DOM事件路径中。您已经在文档事件侦听器中进行了光线追踪。在传播到达#background元素之前执行此操作,如果已知事件已处理,则在某处安全。您已尝试pointer-events:none;。不幸的是,mousemove事件是不可取消的,所以这没有效果。后来调用preventDefault()的事件监听器将获得preventedDefaultfalse的含义是防止用户代理在传播之后执行的默认操作,例如,双击后在鼠标指针下标记单词。如果应该执行默认操作,那么您不能使用preventDefault()告诉其他侦听器该事件已被处理。

您可以将Event的自定义属性设置为true。它至少在firefox中有效,但W3C并未严格指定主机对象。这可能会导致其他浏览器或将来出现一些错误。如果不应该调用其他侦听器,则可以使用preventDefault()。在这种情况下,这可能会导致与某些JavaScript框架相关的不良行为或错误。

另一种方法是在所有事件侦听器都可访问的范围内设置变量,例如:全局范围或更好的封装匿名函数。这可以保存最新处理的事件,并且在传播完成之前不会改变(之前的事件不会被触发)。

如果光线跟踪器没有碰到场景对象,您还可以在文档的侦听器中处理针对 #background 的事件。 stopPropagation()保存由鼠标事件命中的最顶层DOM元素。

您修改过的代码,演示了不同的方法:

Event.target