在另一个元素上层的元素上注册点击事件

23

我有一些元素被放在一个opacity:0.5的元素下面,我希望能够点击最下面的元素。如何使得点击事件“穿透”最上面的元素?

以下是一个演示我的问题的例子。单击方块以切换它们的状态。您可以通过jsbin编辑它,尝试解决该问题。

如果您能让方块在鼠标悬停时切换状态,将获得额外的奖励分数。

<!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> 
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> 
<title>Sandbox</title> 
<meta http-equiv="Content-type" content="text/html; charset=utf-8" /> 
<style type="text/css" media="screen"> 
body { background-color: #000; } 
.box {width: 50px; height: 50px; border: 1px solid white} 
.highlight {background-color: yellow;} 
</style>
<script type="text/javascript">
var dthen = new Date();
$('<div id="past">').css({'height':  (dthen.getMinutes()*60)+dthen.getSeconds() +'px'
            ,'position': 'absolute'
            ,'width': '200px'
            ,'top': '0px'
            ,'background-color': 'grey'
            ,'opacity': '0.5'
            })
        .appendTo("#container");

setInterval(function(){
    dNow = new Date();
    $('#past').css('height', ((dNow.getSeconds()+(dNow.getMilliseconds()/1000))*50)%300 +'px');
},10)

 $(".box").click(function(){
      $(this).toggleClass("highlight");
    });
</script>
</head> 
<body> 
  <div id="container"> 
     <div class="box" style="position:absolute; top: 25px; left: 25px;"></div> 
     <div class="box" style="position:absolute; top: 50px; left: 125px;"></div> 
     <div class="box" style="position:absolute; top: 100px; left: 25px;"></div> 
     <div class="box" style="position:absolute; top: 125px; left: 125px;"></div> 
     <div class="box" style="position:absolute; top: 225px; left: 25px;"></div> 
     <div class="box" style="position:absolute; top: 185px; left: 125px;"></div> 
  </div> 
</body> 
</html>

1
我认为我们不是在这里为你编写完整的解决方案。使用下面给出的建议,你应该自己实现解决方案。SO不是一个免费的咨询店。 - Philippe Leybaert
3
我不相信只有我一个人想要做这件事。因此,我认为展示如何做到这一点的任何代码都会被广泛使用,超出我所计划的范围。我认为要求提供这种代码并没有什么问题。 - Sam Hasler
1
Sam,还是使用addClass而不是所有那些使用.css的设置。 - redsquare
3个回答

32

好的,我认为有两件事情我会做:(1)使用CSS3实现,但这种方法尚未被广泛采用,(2)为其他情况准备一个JavaScript备选方案(不确定如何处理那些没有JS且浏览器不支持CSS的旧版本用户)。请继续阅读...

首先:使用以下CSS3代码使上层元素对鼠标交互“不可见”:

pointer-events: none;

这还不能在所有浏览器上兼容。它只能在Gecko和Webkit浏览器中的SVG和HTML元素上工作,但只能在Opera中的SVG元素上工作,在IE中完全不起作用。

根据MDN:

CSS属性pointer-events允许作者控制元素何时可以成为鼠标事件的目标。此属性用于指定在哪种情况下(如果有)鼠标事件应“穿过”元素并将其目标指向该元素下方的任何内容。

这是文档:https://developer.mozilla.org/en/css/pointer-events

其次:对于那些尚未支持此CSS的浏览器,请使用一些javascript功能检测。您将不得不自己编写类似于Modernizr的功能检测,因为它尚未测试此功能(据我所知)。思路是创建一个元素,将pointer-events CSS应用于它,检查它,如果浏览器支持该功能,则会获得预期结果,而不是null或undefined;然后销毁该元素。无论如何,一旦具有功能检测,就可以使用jQuery或类似的东西将侦听器附加到最上面的元素,并将其单击分配给单击通过到其他内容。请查看jQuery及其对click()函数的使用以获取更多信息(抱歉,没有足够的声望发布链接)。

如果我有时间,我可能会在jsFiddle上快速制作一个示例。希望这可以帮助到您。


1
这个可行!将point-events的解决方案与原始解决方案进行比较。当然,当我在2009年首次提出这个问题时,pointer-events并不存在。IE11是第一个支持pointer-events的版本,它于2013年发布。 - Sam Hasler
如果您需要支持IE10或更低版本,现在Modernizr似乎有一个测试CSS指针事件的功能。 - Sam Hasler
这也解决了悬停问题。您只需将样式附加到.box:hover,即使在#past元素下方仍会触发。 - Sam Hasler

21

如果你的最上层元素被包含在下层元素内部,你可以让事件“冒泡”传递。如果不是,你就必须手动触发点击事件。没有办法通过单击来触发事件处理程序。

要手动触发事件,你可以这样做:

$("#topelement").click(function() {
   $("#elementbelow").click();
   return false;
});

编辑(回应评论):

要枚举所有的盒子,您可以这样做(凭记忆而来,无测试,甚至可能不符合语法)

$("#topelement").click(function(e) {
    $(".box").each(function() {
       // check if clicked point (taken from event) is inside element
       var mouseX = e.pageX;
       var mouseY = e.pageY;
       var offset = $(this).offset;
       var width = $(this).width();
       var height = $(this).height();

       if (mouseX > offset.left && mouseX < offset.left+width 
           && mouseY > offset.top && mouseY < offset.top+height)
         $(this).click(); // force click event
    });
});

你需要枚举所有的DOM元素,以查看点击点是否在一个或多个元素内部。浏览器无法为您完成此操作。 - Philippe Leybaert

2
我已将上述代码修改为jQuery函数,可能有人会发现它有用:
找到替代的点击元素:
$('.child').click(function(e) {
    $(this).siblingsUnderMouse(e).each(fn);
});

如果您想忽略图像中的透明像素:

$('.child').click(function(e) {
    $(this).siblingsUnderMouse(e).each(function() {
        var clickOnVisiblePixel = $(this).isOnVisiblePixel(e);
    });
});

代码:

$.fn.reverse = [].reverse;
$.fn.siblingsUnderMouse = function(e, filter)
{
    filter = filter || '*';

    var arr = $();
    this.parent().children().filter(filter).reverse().each(function()
    {
        var t = $(this);
        // check if clicked point (taken from event) is inside element
        var x = (e.pageX - t.offset().left).toFixed();
        var y = (e.pageY - t.offset().top).toFixed();
        var w = t.width();
        var h = t.height();

        if (x < 0 || x >= w || y < 0 || y >= h)
            return;

        arr.push(t);
    });
    return arr;
}

$.fn.isOnVisiblePixel = function(e)
{
    if (e.type == 'mouseleave') return false;

    var t = this[0];
    if (!t || t.tagName != 'IMG')
        t = this.find('img')[0];
    if (!t) return;

    var w = $(t).width();
    var h = $(t).height();
    var o = $(t).offset();
    var x = (e.pageX - o.left).toFixed();
    var y = (e.pageY - o.top).toFixed();
    if (x < 0 || y < 0 || x >= w || y >= h)
        return false;

    var c = $('<canvas>')[0];
    c.width = w;
    c.height = h;

    var ctx = c.getContext('2d');
    ctx.drawImage(t, 0, 0, w, h);

    var a = ctx.getImageData(x, y, 1, 1).data[3];
    return (a > 128);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接