防止 CSS :hover 样式传播

17
在下面的示例中,当我将鼠标悬停在“X”按钮上时,列表项hover样式也会启用,我不希望这种情况发生。
是否可能使按钮上的hover样式独立于list-group-item上的hover样式?类似于防止“hover”传播的方式?
还有其他方法可以实现吗?也许可以以不同的方式组装所有这些HTML / CSS / JS? 工作样例在此处
<ul class="list-group">
  <li class="list-group-item">
    Lalalalaiaia
                <button class="btn btn-default btn-xs pull-right remove-item">
      <span class="glyphicon glyphicon-remove"></span>
    </button>
  </li>
  <li class="list-group-item">
    Panananannaeue 
                <button class="btn btn-default btn-xs pull-right remove-item">
      <span class="glyphicon glyphicon-remove"></span>
    </button>
  </li>
</ul>

CSS

.list-group-item:hover {
  background: #fafafa;
  cursor: pointer;
}

JavaScript

  $('.list-group-item').on('click', function(){
    console.log('clicked item');
  });

  $('.remove-item').on('click', function(e){
    console.log('clicked remove-item btn');
    e.stopPropagation();
  });

更新

问题似乎在于当鼠标悬停在内部的X按钮上时,鼠标实际上并没有离开'list-group-item'元素,因此它保持了悬停状态。

我通过在'remove-item'按钮的'mouseleave'和'mouseenter'事件中分别手动调度'list-group-item'上的'mouseenter'和'mouseleave'来解决了这个问题,而不需要使用'event.stopPropagation()'(除了按钮单击处理程序)。

缺点是我需要为两个元素都设置'mouseenter'和'mouseleave'事件处理程序。最好只使用CSS,但似乎这是不可能的。

我只是不确定这是否是一个干净的解决方案,你觉得呢?

工作样例在此处

CSS

.list-group-item.mouseover {
  background: #fafafa;
  cursor: pointer;
}

.list-group-item .remove-item.mouseover {
  background: #aaf;
  cursor: pointer;
}

JavaScript

  // LIST-ITEM EVENT HANDLERS

  $('.list-group-item').on('mouseenter', function(e){
    $(this).addClass('mouseover');
  }).on('mouseleave', function(e){
    $(this).removeClass('mouseover');
  });

  $('.list-group-item').on('click', function(){
    console.log('clicked item');
  });

  // LIST-ITEM REMOVE BUTTON EVENT HANDLERS

  $('.remove-item').on('mouseenter', function(e){
    $(this).addClass('mouseover');
    $(this).parent().mouseleave();
  }).on('mouseleave', function(e){
    $(this).removeClass('mouseover');
    $(this).parent().mouseenter();
  });

  $('.remove-item').on('click', function(e){
    console.log('clicked remove-item btn');
    e.stopPropagation();
  });

3
请为 .list-group-item:hover button 设计一个样式,以取消群组项本身的悬停样式。 - Pointy
3
@Pointy提到的规则.list-group-item:hover button会改变按钮的CSS样式,而不是列表项的样式,这与OP想要更改的内容不符。他希望当您悬停在按钮上时,列表项悬停不会被触发(或看起来不像被触发)。 - j08691
@j08691 嗯,我想他不希望按钮受到列表组可继承样式的影响,但是看起来我也不确定为什么;背景可能不会更改按钮背景。也许是指针吗? - Pointy
1
@zok 哦,我明白了。所以你想在鼠标悬停在项目上时将其变暗,但当你悬停在按钮上时,它不应该保持项目变暗。这在CSS中很难/不可能实现。我有点想知道它看起来是否有点奇怪;毕竟,光标仍然停留在项目上。 - Pointy
@Pointy 很好的观点(无恶意)。我成功地做到了我想要的事情(请参见我的更新),但也许那会让用户感到困惑。只要按钮上的点击事件停止传播,一切都很好和直观,对吧? - zok
显示剩余3条评论
4个回答

6
这在仅使用CSS的情况下是不可能实现的,除了 @Pointy 所描述的不太干净的方法。您可以通过使用 event.stopPropagation() 在JavaScript中实现此目的。因此,您的悬停样式应该变成一个您在鼠标悬停时切换的类。
这个问题是 css :hover only affect top div of nest 的重复。

是的,我想唯一的方法就是有一个事件处理程序来处理鼠标悬停事件。或者将删除按钮放在li外面但是紧挨着它,但这似乎不是一个好主意。 - zok

4

好的,实际上有一种解决方案只需要使用CSS(不需要HTML或JS)

当鼠标悬停在具有类名“parent”的元素上时,以下选择器仅选择不具有被悬停的类名为“child”的子元素的父元素。

.parent:has(:not(.child:hover)):hover {}

我能看到 :has() 选择器/伪类唯一的问题在于浏览器支持(特别是旧版本)- 因此,在使用它之前,请检查当前兼容性列表,以确定它是否符合您的要求。

我上周看到了这个!目前(在流行的浏览器中)似乎只有 Firefox 缺失此功能。 - zok
1
@zok,是的,但在 Firefox 中,您已经可以手动激活它。因此我相信它很快就会被添加在那里。 - Lord-JulianXLII

3
你可以像Pointy建议的那样制作一个否定子句,但更可靠的解决方案涉及添加一个额外的节点。这个想法是行和按钮成为适当的兄弟姐妹,因为你不能给TextNode添加样式。
<ul class="list-group">
  <li class="list-group-item">
    <div>Lalalalaiaia</div>
    <button class="btn btn-default btn-xs pull-right remove-item">
      <span class="glyphicon glyphicon-remove"></span>
    </button>
  </li>
  <li class="list-group-item">
    <div>Panananannaeue</div>
    <button class="btn btn-default btn-xs pull-right remove-item">
      <span class="glyphicon glyphicon-remove"></span>
    </button>
  </li>
</ul>

现在你可以做到以下事情:
.list-group-item div:hover {
  background: #fafafa;
  cursor: pointer;
}

您需要一些额外的技巧才能将按钮放置在正确的位置,例如:

// untested
.list-group-item {
  position: relative;
}
.list-group-item button {
  position: absolute;
  top: 5px;
  left: 5px;
}

这是个好主意,如果在包裹 div 上添加 display: inline,按钮将会完美对齐,但背景颜色只会应用于文本,而不是整个列表项的背景。 - zok
从列表项中移除任何填充。无论背景是在列表项还是div上都没关系。 - Halcyon

2

我找不到一个适用于所有情况且实现简单的答案。遗憾的是,似乎没有一种纯CSS或需要HTML的特殊安排的一致解决方案。

这里提供了一个jQuery解决方案,它似乎在所有情况下都有效。

任何带有.ui-hoverable类的元素将接收一个不会传播的.ui-hover类。因此,您可以堆叠.ui-hoverable元素,并且只有鼠标下面最顶层的元素才具有.ui-hover 类。

$('.ui-hoverable').each(function() {
    var el = $(this);
    el.on('mousemove', function () {
        var parent = $(event.target).closest('.ui-hoverable');
        if(parent.length && parent[0] == el[0]) {
           el.addClass('ui-hover');
           return;
        }
        el.removeClass('ui-hover');
    });
    el.on('mouseleave', function () {
        el.removeClass('ui-hover');
    });
});

这可以工作是因为mousemove事件会寻找最接近的.ui-hoverable,如果它不是当前元素,则会移除.ui-hover。因此,最上面的元素将接收.ui-hover,而其下面的元素将被移除。

享受使用,如有问题请报告。

谢谢,


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