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

Chr*_*ris 130 javascript css hover touch

我创建了一个带有前一个和下一个按钮的旋转木马,它们始终可见.这些按钮具有悬停状态,它们变为蓝色.在触控设备(如iPad)上,悬停状态是粘性的,因此按下按钮后按钮会保持蓝色.我不希望这样.

  • 我可以为每个按钮添加一个no-hoverontouchend,并使我的CSS像这样:button:not(.no-hover):hover { background-color: blue; }但这可能对性能非常不利,并且不能正确处理Chromebook Pixel(同时具有触摸屏和鼠标)等设备.

  • 我可以添加一个touchdocumentElement,并使我的CSS像这样:html:not(.touch) button:hover { background-color: blue; }但这也不适用于同时具有触摸和鼠标的设备.

我更喜欢的是删除悬停状态ontouchend.但这似乎不太可能.聚焦另一个元素不会消除悬停状态.手动点击另一个元素,但我似乎无法在JavaScript中触发它.

我发现的所有解决方案似乎都不完美.有完美的解决方案吗?

cvr*_*ert 64

一旦CSS媒体查询4级实现,你就可以做到这一点:

@media (hover: hover) {
    button:hover {
        background-color: blue;
    }
}
Run Code Online (Sandbox Code Playgroud)

或者用英语:"如果浏览器支持正确/真实/真实/非模拟悬停(例如,具有类似鼠标的主要输入设备),则在buttons悬停时应用此样式."

由于Media Queries Level 4的这一部分到目前为止只在最前沿的Chrome中实现,我写了一个polyfill来处理这个问题.使用它,您可以将上面的未来派CSS转换为:

html.my-true-hover button:hover {
    background-color: blue;
}
Run Code Online (Sandbox Code Playgroud)

(.no-touch技术的变体)然后使用来自检测支持悬停的相同polyfill的一些客户端JavaScript,您可以相应地切换my-true-hover类的存在:

$(document).on('mq4hsChange', function (e) {
    $(document.documentElement).toggleClass('my-true-hover', e.trueHover);
});
Run Code Online (Sandbox Code Playgroud)

  • 你需要查看http://caniuse.com/#feat=css-media-interaction,你会发现Firefox和IE11不支持它:(所以你需要polyfill (3认同)
  • 这实际上并不能解决双输入设备的问题。(我是在 Surface Book 2 上写这篇文章的。)您可以在通过触摸按下按钮后移动鼠标/触摸板,但这是一个粗制滥造的解决方案。我们确实需要一个仅适用于指针悬停的伪类。 (3认同)
  • 这现在在移动浏览器中得到了广泛的支持,并且效果很好。我认为这应该是公认的答案。 (2认同)

Sjo*_*her 51

您可以通过暂时从DOM中删除链接来删除悬停状态.见http://testbug.handcraft.com/ipad.html


在CSS中你有:

:hover {background:red;}
Run Code Online (Sandbox Code Playgroud)

在JS中你有:

function fix()
{
    var el = this;
    var par = el.parentNode;
    var next = el.nextSibling;
    par.removeChild(el);
    setTimeout(function() {par.insertBefore(el, next);}, 0)
}
Run Code Online (Sandbox Code Playgroud)

然后在你的HTML中你有:

<a href="#" ontouchend="this.onclick=fix">test</a>
Run Code Online (Sandbox Code Playgroud)

  • @Chris好点,我更改了示例以在ontouchend事件中设置onclick处理程序. (3认同)
  • 请考虑在答案中添加最少的示范代码.谢谢!http://stackoverflow.com/help/how-to-answer (3认同)
  • @KevinBorders在某些设备上是的,删除和重新插入元素之间的时间延迟非常明显.不幸的是,我在我的android 4.4设备上发现没有setTimeout这样做不起作用. (2认同)

Tim*_*ora 27

这是一个常见的问题,没有完美的解决方案.悬停行为对鼠标很有用,而且对触摸大多不利.使问题更加复杂的是支持触摸和鼠标(同时,不低于!)的设备,如Chromebook Pixel和Surface.

我发现最干净的解决方案是仅在设备不被视为支持触摸输入时才启用悬停行为.

var isTouch =  !!("ontouchstart" in window) || window.navigator.msMaxTouchPoints > 0;

if( !isTouch ){
    // add class which defines hover behavior
}
Run Code Online (Sandbox Code Playgroud)

当然,你会丢失悬停在可能支持它的设备上.但是,有时悬停影响比链接本身更多,例如,您可能希望在元素悬停时显示菜单.这种方法允许您测试触摸的存在,并可能有条件地附加不同的事件.

我已经在iPhone,iPad,Chromebook Pixel,Surface和各种Android设备上进行了测试.当将通用USB触摸输入(例如手写笔)添加到混音中时,我无法保证它能够正常工作.


Org*_*nda 17

您可以覆盖不支持悬停的设备的悬停效果。喜欢:

.my-thing {
    color: #BADA55;
}

.my-thing:hover {
    color: hotpink;
}

@media (hover: none) {
    .my-thing {
        color: #BADA55;
    }
}
Run Code Online (Sandbox Code Playgroud)

在 iOS 12 上测试和验证

帽子提示/sf/answers/3519954091/指出这一点。


rac*_*hel 10

使用Modernizr,您可以专门针对无触摸设备定位您的悬停:

(注意:这不会在StackOverflow的代码片段系统上运行,请检查jsfiddle)

/* this one is sticky */
#regular:hover, #regular:active {
  opacity: 0.5;
}

/* this one isn't */
html.no-touch #no-touch:hover, #no-touch:active {
  opacity: 0.5;
}
Run Code Online (Sandbox Code Playgroud)

请注意,:active不需要将其作为目标,.no-touch因为它在移动设备和桌面设备上都按预期工作.

  • 问题是,现在具有触摸屏功能的鼠标设备已经到处都是,你不能再依赖这种方法了.但是,我无法看到另一种方式来做到这一点......这是一个相当困难的问题 (2认同)

Daw*_*ski 8

从 2020 年开始,您可以在媒体查询中添加悬停样式

@media (hover: hover) and (pointer: fine) {
    /* css hover class/style */
}
Run Code Online (Sandbox Code Playgroud)

此媒体查询表明样式将适用于不模拟 :hover 的浏览器,因此它不适用于触摸浏览器。


coc*_*ffs 7

4种方式处理移动设备上的粘滞悬停:这是一种can touch根据用户当前输入类型动态添加或删除文档类的方法.它适用于混合设备,用户可以在触摸和鼠标/触控板之间切换:

<script>

;(function(){
    var isTouch = false //var to indicate current input type (is touch versus no touch) 
    var isTouchTimer 
    var curRootClass = '' //var indicating current document root class ("can-touch" or "")

    function addtouchclass(e){
        clearTimeout(isTouchTimer)
        isTouch = true
        if (curRootClass != 'can-touch'){ //add "can-touch' class if it's not already present
            curRootClass = 'can-touch'
            document.documentElement.classList.add(curRootClass)
        }
        isTouchTimer = setTimeout(function(){isTouch = false}, 500) //maintain "istouch" state for 500ms so removetouchclass doesn't get fired immediately following a touch event
    }

    function removetouchclass(e){
        if (!isTouch && curRootClass == 'can-touch'){ //remove 'can-touch' class if not triggered by a touch event and class is present
            isTouch = false
            curRootClass = ''
            document.documentElement.classList.remove('can-touch')
        }
    }

    document.addEventListener('touchstart', addtouchclass, false) //this event only gets called when input type is touch
    document.addEventListener('mouseover', removetouchclass, false) //this event gets called when input type is everything from touch to mouse/ trackpad
})();

</script>
Run Code Online (Sandbox Code Playgroud)


小智 6

$("#elementwithhover").click(function() { 
  // code that makes element or parent slide or otherwise move out from under mouse. 

  $(this).clone(true).insertAfter($(this));
  $(this).remove();
});
Run Code Online (Sandbox Code Playgroud)