禁用移动浏览器上的悬停效果

时间:2011-11-28 04:24:33

标签: javascript hover touch

我正在撰写一个旨在从台式机和平板电脑上使用的网站。当从桌面访问它时,我希望屏幕的可点击区域以:hover效果(不同的背景颜色等)点亮。使用平板电脑,没有鼠标,所以我不希望任何悬停的效果。

问题是,当我点击平板电脑上的某些东西时,浏览器显然有某种“隐形鼠标光标”,它移动到我点击的位置,然后将其留在那里 - 所以我只是轻拍灯悬停效果,直到我点击其他东西。

我在使用鼠标时如何获得悬停效果,但在使用触摸屏时会抑制它们?

如果有人想提出建议,我不想使用用户代理嗅探。同样的设备可能同时具有触摸屏和鼠标(今天可能不常见,但将来会更加如此)。我对该设备不感兴趣,我对它目前的使用方式感兴趣:鼠标或触摸屏。

我已经尝试挂钩touchstarttouchmovetouchend事件,并在所有事件上调用preventDefault(),这确实会抑制“隐形鼠标光标”时间;但是如果我在两个不同的元素之间来回快速点击,经过几次点击它就会开始移动“鼠标光标”并点亮悬停效果 - 就像我的preventDefault并不总是被尊重。除非必要,否则我不会厌倦你的细节 - 我甚至不确定这是正确的方法;如果有人有更简单的解决方案,我会全力以赴。


编辑:这可以使用沼泽标准CSS :hover进行复制,但这里有一个快速的重复参考。

<style>
  .box { border: 1px solid black; width: 150px; height: 150px; }
  .box:hover { background: blue; }
</style>
<div class="box"></div>
<div class="box"></div>

如果你鼠标悬停在其中任何一个方框上,它将获得我想要的蓝色背景。但是如果你点击其中任何一个盒子,它也会得到一个蓝色背景,这是我试图阻止的。

我还发布了一个示例here来执行上述操作并挂钩jQuery的鼠标事件。您可以使用它来查看点击事件也会触发mouseentermousemovemouseleave

19 个答案:

答案 0 :(得分:82)

我从你的问题中得知你的悬停效果会改变你网页的内容。在这种情况下,我的建议是:

  • touchstartmouseenter上添加悬停效果。
  • 删除mouseleavetouchmoveclick上的悬停效果。

或者,您可以编辑没有内容更改的页面。

背景

为了模拟鼠标,如果用户在触摸屏上触摸并释放手指(如iPad),Webkit移动等浏览器会触发以下事件(来源:html5rocks.com上的Touch And Mouse):

  1. touchstart
  2. touchmove
  3. touchend
  4. 300毫秒延迟,浏览器确保这是一次点击,而不是双击
  5. mouseover
  6. mouseenter
    • 注意:如果mouseovermouseentermousemove事件更改了网页内容,则永远不会触发以下事件。
  7. mousemove
  8. mousedown
  9. mouseup
  10. click
  11. 似乎无法简单地告诉webbrowser跳过鼠标事件。

    更糟糕的是,如果mouseover事件更改了页面内容,则不会触发click事件,如Safari Web Content Guide - Handling Events所述,特别是 One-Finger Events 中的图6.4。究竟什么是“内容更改”,取决于浏览器和版本。我发现对于iOS 7.0,背景颜色的变化不是(或不再是?)内容变化。

    解决方案

    回顾一下:

    • touchstartmouseenter添加悬停效果。
    • 删除mouseleavetouchmoveclick上的悬停效果。

    请注意,touchend上没有任何操作!

    这显然适用于鼠标事件:mouseentermouseleavemouseovermouseout稍微改进的版本)会被触发,并添加和删除悬停。

    如果用户实际click是一个链接,则还会删除悬停效果。这可以确保在用户按下Web浏览器中的后退按钮时将其删除。

    这也适用于触摸事件:在touchstart上添加了悬停效果。在touchend上没有被删除。它会再次添加到mouseenter,因为这不会导致内容更改(已添加),所以click事件也会被触发,并且会跟踪链接,而无需用户再次单击!

    浏览器在touchstart事件和click之间的300毫秒延迟实际上得到了充分利用,因为悬停效果将在这么短的时间内显示。

    如果用户决定取消点击,则手指的移动将正常进行。通常,这是一个问题,因为没有触发mouseleave事件,并且悬停效果仍然存在。值得庆幸的是,通过删除touchmove上的悬停效果,可以轻松解决此问题。

    就是这样!

    请注意,可以删除300毫秒延迟,例如使用FastClick library,但这超出了此问题的范围。

    替代解决方案

    我发现以下替代方案存在以下问题:

    • 浏览器检测:极易出错。假设设备具有鼠标或触摸功能,而当触控显示器增加时,两者的组合将变得越来越常见。
    • CSS媒体检测:我唯一知道的仅限CSS的解决方案。仍然容易出错,并且仍假设设备有鼠标或触摸,但两者都是可能的。
    • 模拟touchend中的点击事件:这将错误地跟踪链接,即使用户只想滚动或缩放,而无意实际点击链接。
    • 使用变量来抑制鼠标事件:这会在touchend中设置一个变量,该变量在后续鼠标事件中用作if条件,以防止在该时间点发生状态更改。该变量在click事件中重置。请参阅Walter Roman在此页面上的回答。如果您真的不希望在触摸界面上悬停效果,这是一个不错的解决方案。不幸的是,如果touchend由于其他原因被触发并且没有触发点击事件(例如用户滚动或缩放),并且随后尝试使用鼠标跟踪链接(即在设备上鼠标和触摸界面)。

    进一步阅读

答案 1 :(得分:19)

  

我在使用鼠标时如何获得悬停效果,但在我使用触摸屏时会抑制它们?

也许不要把它想象成抑制触摸屏的悬停效果,而是为鼠标事件添加悬停效果?

如果您想在CSS中保留:hover效果,可以为不同媒体指定不同的样式:

@media screen { /* hover styles here */ } 

@media handheld { /* non-hover styles here */ }

不幸的是,有很多移动设备忽略了这一点,只是使用了屏幕规则。幸运的是,许多较新的移动/平板电脑浏览器确实支持一些更高级的媒体查询:

@media screen and (max-width:800px) { /* non-hover styles here */ }

所以即使&#34;屏幕&#34;或者&#34;掌上电脑&#34;部分被忽略了&#34; max-width&#34;会为你做的伎俩。您可以假设屏幕小于800像素的任何内容都必须是平板电脑或手机,而不是使用悬停效果。对于在低分辨率设备上使用鼠标的罕见用户,他们不会看到悬停效果,但您的网站会没有问题。

进一步阅读媒体查询?网上有很多关于此的文章 - 这里有一篇:http://www.alistapart.com/articles/return-of-the-mobile-stylesheet

如果您将悬停效果从CSS中移出并使用JavaScript应用它们,那么您可以专门绑定鼠标事件,和/或您可以根据屏幕尺寸做出一些假设,最坏情况下#34 ;问题&#34;一些正在使用鼠标的用户错过了悬停效果。

答案 2 :(得分:9)

我为最近的一个项目编写了以下JS,这是一个桌面/移动/平板电脑网站,其悬浮效果不应该出现在触摸状态。

下面的mobileNoHoverState模块有一个变量preventMouseover(最初声明为false),当用户触发true事件时,该变量设置为touchstart在元素$target上。

每当preventMouseover事件被触发时,

false就会被设置回mouseover,这样如果用户同时使用他们的触摸屏和鼠标,网站就可以按预期工作。< / p>

我们知道mouseovertouchstart被触发,因为它们是在init内宣布的。

var mobileNoHoverState = function() {

    var hoverClass = 'hover',
        $target = $(".foo"), 
        preventMouseover = false;

    function forTouchstart() {
        preventMouseover = true;
    }

    function forMouseover() {
        if (preventMouseover === false) {
            $(this).addClass(hoverClass);
        } else {
            preventMouseover = false;
        }
    }

    function forMouseout() {
        $(this).removeClass(hoverClass);
    }

    function init() {
        $target.on({
            touchstart  : forTouchstart,
            mouseover   : forMouseover,
            mouseout    : forMouseout
        });                
    }

    return {
        init: init
    };
}();

然后将该模块进一步实例化:

mobileNoHoverState.init();

答案 3 :(得分:5)

我的解决方案是将hover-active css类添加到HTML标记中, 并在所有CSS选择器的开头使用它:hover 并在第一个touchstart事件中删除该类。

http://codepen.io/Bnaya/pen/EoJlb

JS:

(function () {
    'use strict';

    if (!('addEventListener' in window)) {
        return;
    }

    var htmlElement = document.querySelector('html');

    function touchStart () {
        document.querySelector('html').classList.remove('hover-active');

        htmlElement.removeEventListener('touchstart', touchStart);
    }

    htmlElement.addEventListener('touchstart', touchStart);
}());

HTML:

<html class="hover-active">

CSS:

.hover-active .mybutton:hover {
    box-shadow: 1px 1px 1px #000;
}

答案 4 :(得分:5)

我自己真的想要一个纯粹的css解决方案,因为在我的所有视图周围散布一个重量级的JavaScript解决方案似乎是一个令人不快的选择。最后找到@media.hover查询,它可以检测主要输入机制是否允许用户将鼠标悬停在元素上。&#34;这可以避免触摸设备“徘徊”。更多的是模拟动作而不是输入设备的直接功能。

例如,如果我有一个链接:

<a href="/" class="link">Home</a>

然后,当设备使用此:hover轻松支持时,我可以安全地将其设置为css

@media (hover: hover) {
  .link:hover { /* hover styles */ }
}

虽然大多数现代浏览器都支持互动媒体功能查询,但一些流行的浏览器such as IE and Firefox却不支持。在我的情况下,这很好用,因为我只打算在桌面上支持Chrome,在移动设备上支持Chrome和Safari。

答案 5 :(得分:2)

我为解决同样的问题所做的是进行特征检测(我使用this code之类的东西),看看是否定义了onTouchMove,如果是,我将css类“touchMode”添加到正文,否则我添加“desktopMode”。

然后,每次某些样式效果仅适用于触摸设备,或者仅适用于桌面时,css规则会附加适当的类:

.desktopMode .someClass:hover{ color: red }
.touchMode .mainDiv { width: 100%; margin: 0; /*etc.*/ }

编辑:这个策略当然会为你的css添加一些额外的字符,所以如果你关心css大小,你可以搜索touchMode和desktopMode定义并将它们放到不同的文件中,因此您可以为每种设备类型提供优化的css;或者你可以在去刺激之前将类名改为更短的东西。

答案 6 :(得分:2)

在我的项目中,我们使用https://www.npmjs.com/package/postcss-hover-prefixhttps://modernizr.com/解决了这个问题 首先,我们使用postcss-hover-prefix对输出css文件进行后期处理。它会为所有css .no-touch规则添加hover

const fs = require("fs");
const postcss = require("postcss");
const hoverPrfx = require("postcss-hover-prefix");

var css = fs.readFileSync(cssFileName, "utf8").toString();
postcss()
   .use(hoverPrfx("no-touch"))
   .process(css)
   .then((result) => {
      fs.writeFileSync(cssFileName, result);
   });

CSS

a.text-primary:hover {
  color: #62686d;
}

变为

.no-touch a.text-primary:hover {
  color: #62686d;
}

在运行时Modernizr会自动将css类添加到html标记中,如此

<html class="wpfe-full-height js flexbox flexboxlegacy canvas canvastext webgl 
  no-touch 
  geolocation postmessage websqldatabase indexeddb hashchange
  history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage
  borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients
  cssreflections csstransforms csstransforms3d csstransitions fontface
  generatedcontent video audio localstorage sessionstorage webworkers
  applicationcache svg inlinesvg smil svgclippaths websocketsbinary">

css plus Modernizr的这种后处理禁用了用于触摸设备的悬停并且能够为其他设备启用。事实上,这种方法受到Bootstrap 4的启发,他们如何解决同样的问题:https://v4-alpha.getbootstrap.com/getting-started/browsers-devices/#sticky-hoverfocus-on-mobile

答案 7 :(得分:1)

是的,我有一个类似的问题,但设法用媒体查询和简单的CSS修复它。我确定我在这里违反了一些规则,但它对我有用。

我基本上不得不接受某人制作的大量应用程序,并使其具有响应性。他们使用jQueryUI并要求我不要篡改他们的任何jQuery,所以我被限制只使用CSS。

当我在触摸屏模式下按下其中一个按钮时,悬停效果会在按钮动作生效前触发一秒钟。这就是我修复它的方法。

@media only screen and (max-width:1024px) {

       #buttonOne{
            height: 44px;
        }


        #buttonOne:hover{
            display:none;
        }
}   

答案 8 :(得分:1)

我发现了2个问题的解决方案,它暗示你用modernizr或其他东西检测触摸,并在html元素上设置触摸类。

这很好但不是 supported

html.touch *:hover {
    all:unset!important;
}

但这有一个非常好的 support

html.touch *:hover {
    pointer-events: none !important;
}

对我而言完美无瑕,它使所有的悬停效果就像当你触摸一个按钮时它会点亮但不会因为鼠标事件的初始悬停效果而失败。

检测无触摸设备的触摸我认为modernizr做得最好:

<击>的 https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js

修改

我找到了一个更好,更简单的解决方案

How to determine if the client is a touch device

答案 9 :(得分:1)

每当触摸触摸屏上的元素时,您都可以触发mouseLeave事件。以下是所有<a>代码的解决方案:

function removeHover() {
    var anchors = document.getElementsByTagName('a');
    for(i=0; i<anchors.length; i++) {
        anchors[i].addEventListener('touchstart', function(e){
            $('a').mouseleave();
        }, false);
    }
}

答案 10 :(得分:0)

在我最近做的一个项目中,我用jQuery的委托事件功能解决了这个问题。它使用jQuery选择器查找某些元素,并在鼠标悬停在元素上时向这些元素添加/删除CSS类。它似乎运行良好,因为我已经能够测试它,其中包括运行Windows 8的触控式笔记本电脑上的IE10。

$(document).ready(
    function()
    {
        // insert your own selector here: maybe '.hoverable'?
        var selector = 'button, .hotspot';

        $('body')
            .on('mouseover', selector, function(){ $(this).addClass('mouseover');    })
            .on('mouseout',  selector, function(){ $(this).removeClass('mouseover'); })
            .on('click',     selector, function(){ $(this).removeClass('mouseover'); });
    }
);

编辑:此解决方案当然要求您更改CSS以删除“:hover”选择器,并提前考虑您希望“可悬停”的元素”

如果你的页面上有很多元素(比如几千个),它可能会有点慢,因为这个解决方案会在页面中的所有元素上捕获三种类型的事件,然后如果选择器匹配,它会做什么。我将CSS类命名为“mouseover”而不是“hover”,因为我不希望任何CSS读者阅读“:hover”我写“.hover”的地方。

答案 11 :(得分:0)

如果您愿意使用JavaScript,则可以在页面中使用Modernizr。当页面加载时,非触摸屏浏览器会在html标签中添加“.no-touch”类,但对于触摸屏浏览器,html标签会将类“.touch”添加到html标签中。

然后,只是在决定添加mouseenter和mouseleave监听器之前检查html标签是否具有无触摸类的情况。

if($('html').hasClass('no-touch')){
    $('.box').on("mouseenter", function(event){
            $(this).css('background-color','#0000ff')
    });
    $('.box').on("mouseleave", function(event){
            $(this).css('background-color','')
    });
}

对于触摸屏设备,事件将没有侦听器,因此当您点击时,您将不会获得悬停效果。

答案 12 :(得分:0)

以下是我的解决方案:http://jsfiddle.net/agamemnus/g56aw709/--以下代码。

所有人需要做的就是将他们的“:hover”转换为“.hover”......就是这样!这与其余部分的最大区别在于,这也适用于非奇异元素选择器,例如.my_class > *:hover {

handle_css_hover_effects ()

function handle_css_hover_effects (init) {
 var init = init || {}
 var handle_touch_events = init.handle_touch_events || true
 var handle_mouse_events = init.handle_mouse_events || true
 var hover_class         = init.hover_class         || "hover"
 var delay_preferences   = init.delay_preferences   || {touch: {add: 500, remove: 500}}
 function default_handler (curobj, input_type, op) {
  var hovered_element_selector = "*" + ((op == "add") ? ":" : ("." + hover_class))
  var hovered_elements = Array.prototype.slice.call(document.body.querySelectorAll(hovered_element_selector))
  var modified_list = []
  while (true) {
   if ((curobj == null) || (curobj == document.documentElement)) break
   if (hovered_elements.indexOf(curobj) != -1) modified_list.push (curobj)
   curobj = curobj.parentNode
  }
  function do_hover_change () {modified_list.forEach (function (curobj) {curobj.classList[op](hover_class)})}
  if ((!delay_preferences[input_type]) || (!delay_preferences[input_type][op])) {
   do_hover_change ()
  } else {
   setTimeout (do_hover_change, delay_preferences[input_type][op])
  }
 }

 if (handle_mouse_events) {
  document.body.addEventListener ('mouseover' , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "add")})
  document.body.addEventListener ('mouseout'  , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "remove")})
  document.body.addEventListener ('click'     , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "remove")})
 }

 if (handle_touch_events) {
  document.body.addEventListener ('touchstart', function (evt) {var curobj = evt.target; default_handler (curobj, "touch", "add")})
  document.body.addEventListener ('touchend'  , function (evt) {var curobj = evt.target; default_handler (curobj, "touch", "remove")})
  document.body.addEventListener ('touchmove',  function (evt) {
   var curobj = evt.target
   var hovered_elements = Array.prototype.slice.call(document.body.querySelectorAll("*:hover"))
   var lastobj = null
   evt = evt.changedTouches[0]
   var elements_at_point = get_elements_at_point (evt.pageX, evt.pageY)
   // Get the last element that isn't at the current point but is still hovered over, and remove only its hover attribute.
   while (true) {
    if ((curobj == null) || (curobj == document.documentElement)) break
    if ((hovered_elements.indexOf(curobj) != -1) && (elements_at_point.indexOf(curobj) == -1)) lastobj = curobj
    curobj = curobj.parentNode
   }
   if (lastobj == null) return
   if ((!delay_preferences.touch) || (!delay_preferences.touch.remove)) {
    lastobj.classList.remove(hover_class)
   } else {
    setTimeout (function () {lastobj.classList.remove(hover_class)}, delay_preferences.touch.remove)
   }

   function get_elements_at_point (x, y) {
    var el_list = [], pe_list = []
    while (true) {
     var curobj = document.elementFromPoint(x, y)
     if ((curobj == null) || (curobj == document.documentElement)) break
     el_list.push (curobj); pe_list.push (curobj.style.pointerEvents)
     curobj.style.pointerEvents = "none"
    }
    el_list.forEach (function (current_element, i) {current_element.style.pointerEvents = pe_list[i]})
    return el_list
   }
  })
 }
}

答案 13 :(得分:0)

它可能有助于查看您的CSS,因为它听起来像一个相当奇怪的问题。但无论如何,如果它正在发生而其他一切都很好,你可以尝试将悬停效果转移到javascript(你也可以使用jquery)。 简单地说,绑定到鼠标悬停或更好的mouseenter事件,并在事件触发时点亮您的元素。

在这里查看最后一个示例:http://api.jquery.com/mouseover/,您可以在事件触发时使用类似于日志的内容并从那里获取它!

答案 14 :(得分:0)

在您的网页上加入Modernizr,然后设置这样的悬停状态:

//to inherit all methods from an object
Function.prototype.extends = function(object){
    for(var property in object.prototype){
        this.prototype[property] = object.prototype[property];
    }
};

var xyz;
$( document ).ready(function(){
    xyz = new xyz_controller();
});

答案 15 :(得分:0)

您好以后的人,您可能想要使用pointer和/或hover媒体查询。不推荐使用handheld媒体查询。

/* device is using a mouse or similar */
@media (pointer: fine) {
  a:hover {
    background: red;
  }
}

答案 16 :(得分:0)

尝试这种简单的2019 jquery解决方案,尽管已经有一段时间了;

  1. 将此插件添加到头部:

    src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"

  2. 将此添加到js:

    $("*").on("touchend", function(e) { $(this).focus(); }); //applies to all elements

  3. 对此的一些建议变体是:

    $(":input, :checkbox,").on("touchend", function(e) {(this).focus);}); //specify elements
    
    $("*").on("click, touchend", function(e) { $(this).focus(); });  //include click event`
    
    css: body { cursor: pointer; } //touch anywhere to end a focus`
    

注释

  • 将插件放在bootstrap.js之前(如果适用),以避免影响工具提示
  • 仅在使用Safari或Chrome的iphone XR ios 12.1.12和ipad 3 ios 9.3.5上进行了测试。

参考文献:

https://code.jquery.com/ui/

https://api.jquery.com/category/selectors/jquery-selector-extensions/

答案 17 :(得分:0)

.services-list .fa {
    transition: 0.5s;
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
    color: blue;
}
/* For me, @media query is the easiest way for disabling hover on mobile devices */
@media only screen and (min-width: 981px) {
    .services-list .fa:hover {
        color: #faa152;
        transition: 0.5s;
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}
/* You can actiate hover on mobile with :active */
.services-list .fa:active {
    color: #faa152;
    transition: 0.5s;
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
}
.services-list .fa-car {
  font-size:20px;
  margin-right:15px;
}
.services-list .fa-user {
  font-size:48px;
  margin-right:15px;
}
.services-list .fa-mobile {
  font-size:60px;
}
<head>
<title>Hover effects on mobile browsers</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>

<body>
<div class="services-list">
<i class="fa fa-car"></i>
<i class="fa fa-user"></i>
<i class="fa fa-mobile"></i>
</div>
</body>

例如:https://jsfiddle.net/lesac4/jg9f4c5r/8/

答案 18 :(得分:0)

您可以使用js。它应该可以正常工作。

function myFunction(){
    var x = document.getElementById("DIV");
  x.style.backgroundColor="red";
  x.style.cursor="pointer";
  x.style.color="white"
}
function my2Function(){
    var x = document.getElementById("DIV");
  x.style.backgroundColor="white";
  x.style.color="red"
}
.mydiv {
  background-color: white;
  color: red;
}
<div class = "mydiv" id="DIV" onmouseover="myFunction()" onmouseleave="my2Function()">
  hi
</div>