不要在子元素上触发悬停事件。

8
我有一个包含瓷砖(草、水等)的容器,其中一些瓷砖上有物品(边框精灵、树木和一般物品)。由于精灵的性质,所有物品都是64x64,而瓷砖只有32x32。

我的问题是,我想在瓷砖上触发悬停事件,但有时它们会被另一个瓷砖的物品遮挡。

下面的图像显示了问题。粗绿色区域是当我想悬停在树瓦片上时真正被悬停的瓦片。

问题 http://upload.cip.nu/pfile.php?file_id=2327

CSS:

#map_canvas .tile{
    height: 32px;
    position: absolute;
    width: 32px;
}
#map_canvas .tile .item{
    background-position: bottom right;
    background-repeat: no-repeat;
    bottom: 0;
    display: inline-block;
    height: 64px;
    margin: 0;
    padding: 0;
    position: absolute;
    right: 0;
}

HTML,简化版:

<div class="tile" style="background-image: url(grass.png);"></div>
<div class="tile" style="background-image: url(grass.png);"></div>
<div class="tile" style="background-image: url(grass.png);">
    <div class="item" style="background-image(top-left-border.png);"></div>
</div>

这是一个实时演示 http://sleavely.com/tiles/

我甚至不知道如何表达问题,但还是试一试:

是否可能仅在父元素(.tile)上触发事件,以便溢出的子元素(.item)不会混淆我真正悬停的瓷砖?

编辑:感谢@Brilliand,我实施了以下操作

function figureOutTile(e){
    var $source = jQuery(e.srcElement);
    if($source.hasClass("item")){
        var $parent = $source.parent();
        var posx = $parent.attr("col");
        var posy = $parent.attr("row");
        if(e.offsetY <= 32){
            if(e.offsetX <= 32){
                return jQuery(".tile[col="+ (posx-1) +"][row="+ (posy-1) +"]");
            }else{
                return jQuery(".tile[col="+ posx +"][row="+ (posy-1) +"]");
            }
        }else{
            if(e.offsetX <= 32){
                return jQuery(".tile[col="+ (posx-1) +"][row="+ posy +"]");
            }else{
                return $parent;
            }
        }
    }else{
        return $source;
    }
}
jQuery("#map_viewport").on({
    mouseenter: function(e) {
        var $target = figureOutTile(e);
        $target.addClass('hovered');
    },
    mouseleave: function() {
        jQuery(".tile.hovered").removeClass('hovered');
    }
}, '.tile');​

我们能看一下你的JavaScript代码吗?到目前为止,你发布的代码中没有涉及到hover。 - Fresheyeball
1
哇,这是一个非常具体的问题。但我认为它归结于事件冒泡。 - Jivings
@Fresheyeball:目前基本上只是一个 jQuery("#map_viewport").on("hover", ".tile", function(e){,我用它来在瓷砖上切换类。问题是事件触发在错误的瓷砖上。 - Sleavely
嗯,这些信息对我来说还不够。你能设置一个 jsfiddle 吗? - Fresheyeball
不是很相关于你的问题,但是不要使用 on('hover', function() {...});,而是使用 jQuery 的 hover() 或者 mouseenter / mouseleave - adeneo
@Fresheyeball 我在我的问题底部编辑了一个链接。 - Sleavely
3个回答

3
$(".tile").children().on('mouseenter', function(e) { 
   e.stopPropogation(); 
});

或者
$(".tile").on('mouseenter', function(e) { 
   if (this===e.target) {
      //do stuff
   }
});

EDIT:

$("#map_viewport").on({
    mouseenter: function(e) {
        if (this===e.target) {
            $(element).addClass('someClass')
        }
    },
    mouseleave: function() {
        $(element).removeClass('someClass')
    }
}, '.tile');

我尝试了你的第二个和第三个例子,两个都没有起作用。第三个例子只能触发在没有子节点且周围没有带有物品的方块上。 :( - Sleavely

1

就您的浏览器而言,悬停在瓷砖上突出的东西上与悬停在它所突出的瓷砖上是一样的,而不是悬停在被覆盖的任何东西上。为了解决您特定的情况,我建议将您的悬停功能放在#map_canvas上,并在该功能中确定正在悬停的瓷砖。对于矩形网格中的瓷砖,这是简单的算术。

当然,为了使其正常工作,您需要包括一个mousemove事件(以检测鼠标何时从一个瓷砖移动到另一个瓷砖),并且如果用户仍然悬停在同一个瓷砖上,您应该包括退出函数的代码。

编辑:尽管已经接受了这个答案,但这里有一个基于我的评论的解决方案:

$(".tile").append($("<div/>", {
    css: {
        position: "absolute",
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        "z-index": 50
    }
}));

这似乎可以解决所有带有项目的瓷砖的问题,但它会破坏你的:empty声明。我提到它主要是因为它更接近回答你所问的问题。


也许在每个瓦片上插入一个空的绝对定位div,并赋予高z-index,这样可能能够在悬停事件到达重叠项之前拦截它。 - Brilliand
我想我稍后会尝试这个解决方案,因为它可能根本不需要Javascript。 - Sleavely

0

编辑:如果确实是事件冒泡的问题,并且您正在使用jQuery,请查看event.stopPropagation method

您尝试添加z-index了吗?

    #map_canvas .tile{
        height: 32px;
        position: absolute;
        width: 32px;
        z-index: 1;
    }
    #map_canvas .tile .item{
        background-position: bottom right;
        background-repeat: no-repeat;
        bottom: 0;
        display: inline-block;
        height: 64px;
        margin: 0;
        padding: 0;
        position: absolute;
        right: 0;
        z-index: 2;
    }

event.stopPropagation仅阻止事件冒泡,而不是来自子元素的事件。:(但我不确定z-index在这种情况下的目的。 - Sleavely

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