如何在触摸设备上防止按钮粘滞悬停效果

213

我创建了一个旋转木马,并添加了上一个和下一个按钮,这些按钮始终可见。这些按钮有悬停状态,它们会变成蓝色。在iPad等触摸设备上,悬停状态是粘性的,因此在点击后按钮会保持蓝色。但我不希望出现这种情况。

  • 我可以为每个按钮添加一个no-hover类,并在ontouchend时更改CSS如下:button:not(.no-hover):hover { background-color: blue; } 但这可能对性能影响较大,并且不能正确处理像Chromebook Pixel(具有触摸屏和鼠标)这样的设备。

  • 我可以向documentElement添加touch类,并将我的CSS更改如下:html:not(.touch) button:hover { background-color: blue;} 但是这也不能在同时具有触摸和鼠标的设备上正确工作。

我更喜欢在ontouchend时删除悬停状态。但似乎这是不可能的。将焦点放在另一个元素上并不能删除悬停状态。手动点击其他元素可以,但我似乎无法在JavaScript中触发该事件。

我找到的所有解决方案都不完美。是否有完美的解决方案?


6
https://css-tricks.com/solving-sticky-hover-states-with-media-hover-hover/ - dasfdsa
这是一个相当不错的解决方案,@dasfdsa!然而,它并不适用于同时支持触摸屏和鼠标的设备。 - Chris
如果使用Tailwind CSS,它会像这样:[@media(hover:hover)]:hover:bg-red-400。只有在前面的任意变量为真时,它才会应用该悬停样式。 - zenw0lf
30个回答

0

我认为我已经找到了一个优雅(最小JS)的解决方案,适用于类似的问题:

使用jQuery,您可以使用.mouseover()在body(或任何其他元素)上触发悬停事件

因此,我只需将此处理程序附加到元素的ontouchend事件即可:

var unhover = function() {
  $("body").mousover();  
};
.hoverable {
  width: 100px;
  height: 100px;
  background: teal;
  cursor: pointer;
}

.hoverable:hover {
  background: pink;
}
<div class="hoverable" ontouchend={unhover}></div>

然而,这只会在触发了其他触摸事件(如滑动或另一个触摸)之后才从元素中删除:hover伪类。


2
在您的代码片段中,我点击了正方形后仍然保持为粉色。我猜您并没有真正解决这个问题? - Kevin Lee
这个解决方案不起作用。首先,您使用了错误的方法来调用事件,应该使用.trigger()。其次,在移动Safari上也无法正常工作。 - xHocquet

0

移动设备上一些粘滞或卡住的 :hover :focus :active 问题可能是由于缺少 <meta name="viewport" content="width=device-width"> 导致浏览器试图操纵屏幕。


0

您可以在:active状态下设置背景颜色,并将:focus的默认背景设置为背景颜色。

如果您通过onfocus/ontouch设置背景颜色,则一旦:focus状态消失,颜色样式仍然保持不变。
当焦点丢失时,您需要在onblur上进行重置,以恢复默认背景。


但我希望保留鼠标用户的悬停效果。 - Chris
:hover和:active可以接收相同的CSS,问题出在:focus上。实际上,如果您通过onfocus设置background-color,则颜色样式会在焦点消失后保留。您需要在onblur上进行重置以恢复默认背景。 - G-Cyrillus

0
如果你是一个 CSS-in-JS 的开发者,正在寻找解决这个问题的方法,那么这里有一个方案。
你可以使用 JS 媒体查询 在 CSS-in-JS 中实现媒体查询。
例如,下面的代码片段只会在屏幕尺寸大于 768px 时为按钮添加悬停效果。
tag: {
  cursor: "pointer",
  "&:hover, &:active": window.matchMedia('(min-width: 768px)').matches ? {
    transform: "scale(1.3)"
  } : null
}

0
这对我有用:将悬停样式放入新类中。
.fakehover {background: red}

然后根据需要添加/删除类。
$(".someclass > li").on("mouseenter", function(e) {
  $(this).addClass("fakehover");
});
$(".someclass > li").on("mouseleave", function(e) {
  $(this).removeClass("fakehover");
});

针对touchstart和touchend事件重复操作。或者您可以选择任何事件来获得所需的结果,例如我希望在触摸屏上切换悬停效果。


0

在我的项目中,我所做的是在触摸设备上撤销:hover的更改:

.myhoveredclass {
    background-color:green;
}
.myhoveredclass:hover {
    background-color:red;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
    .myhoveredclass:hover, .myhoveredclass:active, .myhoveredclass:focus {
        background-color:green;
    }
}

所有的类名和命名颜色仅供演示目的 ;-)


0

可以通过交换HTML类来实现。与删除整个元素相比,特别是对于大型图像链接等,应该更不容易出现故障。

我们还可以决定是否希望在触摸滚动(touchmove)时触发hover状态,甚至添加超时以延迟它们。

我们代码中唯一重要的变化将是使用其他HTML类,例如<a class='hover'></a>在实现新行为的元素上。

HTML

<a class='my-link hover' href='#'>
    Test
</a>

CSS(层叠样式表)
.my-link:active, // :active can be turned off to disable hover state on 'touchmove'
.my-link.hover:hover {

    border: 2px dotted grey;
}

JS(使用jQuery)

$('.hover').bind('touchstart', function () {

   var $el;
   $el = $(this);
   $el.removeClass('hover'); 

   $el.hover(null, function () {
      $el.addClass('hover');
   });
});

例子

https://codepen.io/mattrcouk/pen/VweajZv

-

我没有同时具备鼠标和触控功能的设备来进行适当测试。


-1
对于我来说,解决方案是在touchend之后克隆和替换节点......我讨厌这样做,但甚至尝试使用offsetHeight重绘元素也不起作用。
    let cloneNode = originNode.cloneNode( true );
    originNode.parentNode.replaceChild( cloneNode, originNode );

最后,这对我来说行不通...在我的情况下,我必须克隆一个扩展了许多事件监听器的整个ES6,解决这样微小的问题变得太复杂了。 - Banning

-1

使用JavaScript太容易了。那不是悬停问题,而是焦点问题。在使用CSS时,将轮廓设置为无。

.button:focus {
outline: none;
}    

-2

Kevin Lee的答案包括一个在其他任何地方都没有看到的结尾部分,它帮助我非常轻松地解决了用于多输入系统(如触摸屏笔记本电脑)。

function f() {
  console.log("Hi");
}
button:hover {
  border: 2px solid blue;
}
<button type="button" onclick="f()" ontouchend="f(); event.preventDefault()">
  Say Hi
</button>

在多输入设备上运行此程序,您应该看到使用鼠标指针悬停会创建蓝色边框,但在触摸屏幕上按下按钮则不会。

唯一的缺点是您的 UI 框架中的任何其他单击功能(涟漪动画等)可能不会触发。您可以尝试手动触发它们 - 在我的情况下,这本来就不是问题。


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