如何在支持触摸的浏览器中模拟鼠标悬停效果?

128

有一些 HTML 如下:

<p>Some Text</p>

然后添加以下 CSS:

p {
  color:black;
}

p:hover {
  color:red;
}

如何在触控设备上允许长按来复制悬停效果?我可以更改标记/使用JS等,但是想不到一个简单的方法来实现这一点。


我特别考虑的是iPhone OS - 它是CSS动画的演示,对于许多人来说,使用悬停而不是点击开始更容易。 - Rich Bradshaw
15个回答

185

好的,我已经弄明白了!它涉及到稍微更改一下CSS并添加一些JS。

使用jQuery可以使其更容易:

$(document).ready(function() {
    $('.hover').on('touchstart touchend', function(e) {
        e.preventDefault();
        $(this).toggleClass('hover_effect');
    });
});

在中文:当您开始或结束触摸时,打开或关闭类名为hover_effect的样式。
然后,在您的HTML中,将悬停类添加到您想要使用此功能的任何内容中。在您的CSS中,替换任何实例:
element:hover {
    rule:properties;
}

使用

element:hover, element.hover_effect {
    rule:properties;
}

为了增加实用性,还可以将以下内容添加到您的CSS中:

.hover {
-webkit-user-select: none;
-webkit-touch-callout: none;        
}

停止浏览器要求你复制/保存/选择图像或其他内容的提示。
很简单!

这很棒。我将domready函数分开了,因为如果用户触摸一个元素并拖到另一个元素上(例如,如果他们触摸了错误的项目并试图取消触摸),toggle会使事情变得混乱。 - Jacksonkr
我还想请教一下如何在用户触摸其他地方或开始滚动时关闭这样的菜单。我猜可以在body(或类似的元素)上添加另一个touchstart监听器,但我想知道是否有更好的方法。谢谢! - Darryl Young
+1 增加了 user-selecttouch-callout 的修复功能。大约一年前,我曾经拼命寻找这种功能,但却无处可找。 - myfunkyside
2
有没有办法忽略滚动手势?这正是我需要的,但现在我无法滚动具有此效果的内容。 - alsobubbly
1
我已经移除了preventDefault,因为我想要滚动功能可用,但是感谢你在touchstart和touchend方面的建议!这真是神器,这是唯一一个在所有浏览器中可靠工作的答案。 - Petraeus
显示剩余2条评论

56

你所需做的就是在父元素上绑定touchstart事件。像下面这样写即可:

$('body').on('touchstart', function() {});

你不需要在函数中做任何事情,将其留空即可。这样就足以获得触摸悬停效果,因此触摸行为更像:hover而不是:active。iOS魔法。


3
这个解决方案是否允许在第二次点击时执行链接? - samuelkobe
1
不,它仍然在第一次触摸时触发点击。我刚刚测试过了。 - Jack
太棒了!快速简单。 - Hunter Turner

42

试试这个:

<script>
document.addEventListener("touchstart", function(){}, true);
</script>

并且在你的CSS中:

element:hover, element:active {
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-user-select: none;
-webkit-touch-callout: none /*only to disable context menu on long press*/
}

使用这段代码,您不需要额外的 .hover 类!


1
这对我很有效。还有一些其他的东西可能会更有帮助,而不是添加JavaScript,可以这样做:然后在CSS的:active事件中添加一个小计时器: tr:active { background-color:#118aab; transition:all .2s linear; -webkit-tap-highlight-color:rgba(0,0,0,0); -webkit-user-select:none; -webkit-touch-callout:none } - Kozy
对我有用。谢谢...唯一的问题是它有点卡顿...我的意思是,按下后它不会立即加载。 - Roman Losev
使用 fastclick.js 来消除延迟。 - Anselm

25
回答你的主要问题:“如何在触摸可用的浏览器中模拟悬停?” 只需允许通过点击屏幕来“点击”元素,然后使用JavaScript触发hover事件即可。
var p = document.getElementsByTagName('p')[0];
p.onclick = function() {
 // Trigger the `hover` event on the paragraph
 p.onhover.call(p);
};

只要设备上有hover事件(尽管通常不会使用),这应该可以工作。

更新: 我刚在我的iPhone上测试了这种技术,似乎运行良好。在这里尝试一下:http://jsfiddle.net/mathias/YS7ft/show/light/

如果你想使用“长按”来触发悬停效果,你可以以上面的代码片段为起点,玩弄定时器等东西 :)


我不知道那个onhover.call(p)的部分,那很酷。但是没有离开悬停... - Rich Bradshaw
你已经测试了多个元素和长按吗?也就是用户在屏幕上移动手指,每个元素都会显示悬停样式? - Marcus
似乎如果您已经在使用jQuery,您可以直接使用jQuery本身调用hover事件,例如:jQuery.hover(); 不过我不确定API是否支持这样做。 - Kzqai
抱歉,那是一个更适用于上面使用jQuery的帖子的评论。 - Kzqai
你需要有任何特殊的脚本或调用函数吗?它对我有效,但只在一个页面上,我不知道区别在哪里。 - Richard Young

14

进一步改进的解决方案

起初我采用了Rich Bradshaw 的方法,但问题开始出现。通过在'touchstart'事件上使用e.preventDefault(),页面不再滚动,长按也无法触发选项菜单,双击缩放也无法完成执行。

一个解决方案是找出正在被调用的事件,并仅在后续事件'touchend' 中使用 e.preventDefault()。由于滚动的'touchmove''touchend' 之前进行,默认情况下保持不变,而'click' 也被阻止,因为它在应用于移动设备的事件链中位于后面,如下所示:

// Binding to the '.static_parent' ensuring dynamic ajaxified content
$('.static_parent').on('touchstart touchend', '.link', function (e) {

    // If event is 'touchend' then...
    if (e.type == 'touchend') {
        // Ensuring we event prevent default in all major browsers
        e.preventDefault ? e.preventDefault() : e.returnValue = false;
    }

    // Add class responsible for :hover effect
    $(this).toggleClass('hover_effect');
});

但是,当选项菜单出现时,它不再触发负责切换类的'touchend'事件,下一次悬停行为将完全混乱无序。

解决方案是,再次条件性地找出我们处于哪个事件中,或者只是分别使用 addClass()removeClass()'touchstart''touchend' 上,确保始终通过添加和删除来开始和结束,而不是有条件地决定。最后,我们还将把删除回调绑定到 'focusout' 事件类型上,负责清除可能仍然保留且永远不会被重新访问的任何链接的悬停类,例如:

$('.static_parent').on('touchstart', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('touchend focusout', '.link', function (e) {
    // Think double click zoom still fails here
    e.preventDefault ? e.preventDefault() : e.returnValue = false;
    $(this).removeClass('hover_effect');
});

注意:两个先前的解决方案中仍然存在一些错误,而且我认为(未经测试)双击缩放仍然会失败。

整洁、干净,但还是有bug的Javascript解决方案

现在,我们采用更为简洁、整洁和响应式的方法,只使用Javascript(不混合使用.hover类和伪类:hover),您可以直接在通用(移动和桌面)的'click'事件上调用您的ajax行为。我发现了一个回答得相当好的问题,最终理解了如何将触摸和鼠标事件混合在一起,而不需要多个事件回调必然改变彼此在事件链上的回调函数。下面是具体实现方法:

$('.static_parent').on('touchstart mouseenter', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('mouseleave touchmove click', '.link', function (e) {
    $(this).removeClass('hover_effect');

    // As it's the chain's last event we only prevent it from making HTTP request
    if (e.type == 'click') {
        e.preventDefault ? e.preventDefault() : e.returnValue = false;

        // Ajax behavior here!
    }
});

3
老鼠的悬停效果不能在触摸设备上实现。当我在Safari iOS中遇到相同情况时,我使用CSS中的:active来制作效果。
即。
p:active {
  color:red;
}

在我的情况下,它是有效的。也许这种情况可以在不使用javascript的情况下使用。试一试吧。

2

添加以下代码,然后将类“tapHover”设置为您希望以此方式工作的元素。 第一次轻触元素时,它将获得伪类“:hover”和类“tapped”。单击事件将被阻止。 第二次轻触相同的元素-单击事件将被触发。

// Activate only in devices with touch screen
if('ontouchstart' in window)
{
    // this will make touch event add hover pseudoclass
    document.addEventListener('touchstart', function(e) {}, true);

    // modify click event
    document.addEventListener('click', function(e) {
        // get .tapHover element under cursor
        var el = jQuery(e.target).hasClass('tapHover')
            ? jQuery(e.target)
            : jQuery(e.target).closest('.tapHover');

        if(!el.length)
            return;

        // remove tapped class from old ones
        jQuery('.tapHover.tapped').each(function() {
            if(this != el.get(0))
                jQuery(this).removeClass('tapped');
        });

        if(!el.hasClass('tapped'))
        {
            // this is the first tap
            el.addClass('tapped');
            e.preventDefault();
            return false;
        }
        else
        {
            // second tap
            return true;
        }
    }, true);
}
.box {
 float:  left;
 
 display: inline-block;
 margin:  50px 0 0 50px;
 width:  100px;
 height:  100px;
 overflow: hidden;
 
 font-size: 20px;
 
 border:  solid 1px black;
}
.box.tapHover {
 background: yellow;
}
.box.tapped {
 border:  solid 3px red;
}
.box:hover {
 background: red;
}
<div class="box" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>


2

我个人的喜好是将:hover样式也应用到:focus状态,例如:

p {
    color: red;
}

p:hover, p:focus {
    color: blue;
}

然后使用以下HTML代码:
<p id="some-p-tag" tabindex="-1">WOOOO</p>

以下是JavaScript代码:

$("#some-p-tag").on("touchstart", function(e){
    e.preventDefault();
    var $elem = $(this);

    if($elem.is(":focus")) {
        //this can be registered as a "click" on a mobile device, as it's a double tap
        $elem.blur()
    }
    else {
        $elem.focus();
    }
});

1

没有特定于设备(或浏览器)的JS,我相信你运气不佳。

编辑:我以为你想避免这种情况,直到我重新阅读了你的问题。在移动Safari的情况下,您可以注册以获取所有触摸事件,类似于您可以使用本机UIView-s做的事情。现在找不到文档,但会尝试。


1

一种方法是在触摸开始时执行悬停效果,然后在触摸移动或结束时删除悬停效果。

这是苹果关于触摸处理的一般说法,因为您提到了iPhone。


链接内容现在是否仍然相关? - Flavien Volken

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