【问题标题】:How to prevent sticky hover effects for buttons on touch devices如何防止触摸设备上按钮的粘滞悬停效果
【发布时间】:2013-06-18 12:00:08
【问题描述】:

我创建了一个带有始终可见的上一个和下一个按钮的轮播。这些按钮有一个悬停状态,它们变成蓝色。在 iPad 等触控设备上,悬停状态是粘性的,因此在点击后按钮会保持蓝色。我不想要那个。

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

  • 我可以在documentElement 中添加一个touch 类并制作我的CSS 像这样:html:not(.touch) button:hover { background-color: blue; } 但这也不适用于同时具备触控和触控功能的设备 鼠标。

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

我找到的所有解决方案似乎都不完美。有完美的解决方案吗?

【问题讨论】:

  • 这是一个非常好的解决方案,@dasfdsa!但是,它不适用于同时支持触摸屏和鼠标的设备。

标签: javascript css hover touch


【解决方案1】:

由于CSS Media Queries Level 4的这部分现在已经是widely implemented since 2018,你可以使用这个:

@media (hover: hover) {
    button:hover {
        background-color: blue;
    }
}

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

对于未实现此功能(或在此原始答案时尚未实现)的浏览器,I wrote a polyfill 处理此问题。使用它,您可以将上述未来主义的 CSS 转换为:

html.my-true-hover button:hover {
    background-color: blue;
}

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

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

【讨论】:

  • 谢谢!出于我的目的,这似乎在大多数浏览器 caniuse.com/#search=media%20queries 中都受支持,并且效果很好,谢谢!
  • 你需要查看caniuse.com/#feat=css-media-interaction,你会发现它在 Firefox 和 IE11 中不受支持 :( 所以你需要 polyfill
  • 似乎不再支持 polyfill(repo 已归档),它需要 jQuery...
  • 这现在在移动浏览器中得到了广泛的支持,并且就像一个魅力。我认为这应该是公认的答案。
  • 这实际上并不能解决双输入设备的问题。 (我是从 Surface Book 2 上写的。)您可以在通过触摸按下按钮后移动鼠标/触摸板,但这是一个粗制滥造的修复。我们真的需要一个仅适用于鼠标悬停的伪类。
【解决方案2】:

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


在您拥有的 CSS 中:

:hover {background:red;}

在你的 JS 中:

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

然后在您的 HTML 中:

<a href="#" ontouchend="this.onclick=fix">test</a>

【讨论】:

  • @Chris 好点,我更改了示例以在 ontouchend 事件中设置 onclick 处理程序。
  • 请考虑在您的答案中添加最少的演示代码。谢谢! stackoverflow.com/help/how-to-answer
  • @SjoerdVisscher 我已将其粘贴进去。StackOverflow 喜欢将代码放在答案中,因为链接可以消失。 (在这种情况下,它不仅需要点击,还需要查看源代码,并确定哪些位是有问题的技术。)
  • @KevinBorders 是的,在某些设备上,移除和重新插入元素之间的时间延迟可能非常明显。不幸的是,我在我的 android 4.4 设备上发现没有 setTimeout 这样做是行不通的。
  • @DarrenCook 但是删除元素并重新添加它是个好主意吗?我猜这会导致“janking”并与应用程序的 60fps 黄油流畅需求发生冲突。
【解决方案3】:

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

我发现的最简洁的解决方案是仅在设备不支持触摸输入时启用悬停行为。

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

if( !isTouch ){
    // add class which defines hover behavior
}

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

我已经在 iPhone、iPad、Chromebook Pixel、Surface 和各种 Android 设备上进行了测试。我无法保证在将通用 USB 触摸输入(例如手写笔)添加到组合中时它会起作用。

【讨论】:

  • 太棒了,这对我有用,不像社区维基/达伦库克的回答。
  • 好答案。这是一个类似的解决方案:stackoverflow.com/a/39787268/885464
  • 不错的解决方案!但不适用于同时支持触摸和鼠标的设备。
【解决方案4】:

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

.my-thing {
    color: #BADA55;
}

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

@media (hover: none) {
    .my-thing {
        color: #BADA55;
    }
}

在 iOS 12 上测试和验证

感谢https://stackoverflow.com/a/50285058/178959 指出这一点。

【讨论】:

  • 这是更现代的解决方案,不需要 javascript 或 DOM 操作,具有完整的浏览器支持(IE 除外),应该是公认的答案。
  • 另请查看:css-tricks.com/…
  • 这也不涉及双模设备(触摸屏笔记本电脑等)。如果浏览器同时具有触摸和指针输入,hover: none 将不成立。
【解决方案5】:

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

(注意:这不能在 StackOverflow 的 sn-p 系统上运行,请查看 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;
}

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

【讨论】:

  • 这是 IMO 的最佳答案,但示例不正确。它为触摸和非触摸设备显示相同的悬停状态。如果.no-touch 出现在html 标签上,它应该只应用悬停状态。否则,这个答案会得到我的认可。
  • 问题是现在有触摸屏的鼠标设备已经无处不在,你不能再依赖这种方法了。但是我看不到太多其他方法......这是一个两难选择
  • 我想一种方法是同时使用 Modernizr 以及诸如 mobile-detect.js 之类的库,以确保它是手机或平板电脑。
  • 你是救世主,非常感谢这对移动设备非常有效
  • 这并不能解决多输入设备的问题。 no-touch 类适用于我的 Surface Book 2(触控板 + 触摸屏),但 :hover 规则保留在您的小提琴链接中。
【解决方案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();
});

【讨论】:

  • 您可以使用 on() 而不是 click() 来改进此答案。通过从 DOM 中删除元素并重新附加它,我丢失了点击事件。当我用 on 重写我的函数时,绑定到元素,然后删除和添加工作。例如:`$('body').on('click', '#elementwithhover',function() { // clone, insertafter, remove });如果你做出改变,我会投票赞成。
  • clone true 保留了新元素上的点击事件,取代了卡住悬停的元素。原始元素在克隆后从 dom 中删除。
  • 很棒的兄弟,10x
  • 数小时搜索修复,这终于提供了一个。谢谢一百万!
【解决方案7】:

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

@media (hover: hover) and (pointer: fine) {
    /* css hover class/style */
}

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

【讨论】:

  • 这适用于触摸浏览器,即没有常规指针可用的触摸屏。它不能解决问题,例如触摸屏笔记本电脑。
【解决方案8】:

来自4 ways to deal with sticky hover on mobile:这是一种根据用户的当前输入类型向文档动态添加或删除“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>

【讨论】:

  • 这是我发现的最好方法,另外,如果你将定时器增加到 1000 毫秒,你还可以覆盖长按,请参阅here。好东西!
【解决方案9】:

我打算发布自己的解决方案,但检查是否有人已经发布,我发现@Rodney 几乎做到了。然而,他错过了最后一个关键,这使它变得不协调,至少在我的情况下。我的意思是,我也通过mouseentermouseleave 事件检测采取了相同的.fakeHover 类添加/删除,但仅此一项,本身,几乎就像“真正的”:hover .我的意思是:当您点击表格中的某个元素时,它不会检测到您“离开”了它 - 从而保持了“fakehover”状态。

我所做的也只是简单地收听click,所以当我“点击”按钮时,我手动触发了mouseleave

这是我的最终代码:

.fakeHover {
    background-color: blue;
}


$(document).on('mouseenter', 'button.myButton',function(){
    $(this).addClass('fakeHover');
});

$(document).on('mouseleave', 'button.myButton',function(){
    $(this).removeClass('fakeHover');
});

$(document).on('button.myButton, 'click', function(){
    $(this).mouseleave();
});

这样,您在使用鼠标时只需将鼠标“悬停”在您的按钮上即可保持您通常的hover 功能。好吧,几乎全部:唯一的缺点是,在用鼠标单击按钮后,它不会处于hover 状态。就像您单击并快速将指针移出按钮一样。但就我而言,我可以忍受。

【讨论】:

    【解决方案10】:

    这是我在研究了其余答案后得出的结论。它应该能够支持仅触摸、仅鼠标或混合用户。

    为悬停效果创建一个单独的悬停类。默认情况下,将此悬停类添加到我们的按钮。

    我们不想从一开始就检测是否存在触摸支持并禁用所有悬停效果。正如其他人所提到的,混合设备越来越受欢迎。人们可能有触摸支持,但想使用鼠标,反之亦然。因此,只有在用户实际触摸按钮时才移除悬停类。

    下一个问题是,如果用户在触摸按钮后想要返回使用鼠标怎么办?为了解决这个问题,我们需要找到一个合适的时机来添加我们已经删除的悬停类。

    但是,我们不能在删除它后立即将其添加回来,因为悬停状态仍然处于活动状态。我们可能也不想销毁并重新创建整个按钮。

    所以,我想到了使用忙等待算法(使用 setInterval)来检查悬停状态。一旦悬停状态被停用,我们就可以添加回悬停类并停止忙等待,使我们回到用户可以使用鼠标或触摸的原始状态。

    我知道忙等待不是很好,但我不确定是否有合适的事件。我考虑将它添加回 mouseleave 事件,但它不是很健壮。例如,当按钮被触摸后弹出警报时,鼠标位置会发生变化,但不会触发 mouseleave 事件。

    var button = document.getElementById('myButton');
    
    button.ontouchstart = function(e) {
      console.log('ontouchstart');
      $('.button').removeClass('button-hover');
      startIntervalToResetHover();
    };
    
    button.onclick = function(e) {
      console.log('onclick');
    }
    
    var intervalId;
    
    function startIntervalToResetHover() {
      // Clear the previous one, if any.
      if (intervalId) {
        clearInterval(intervalId);
      }
      
      intervalId = setInterval(function() {
        // Stop if the hover class already exists.
        if ($('.button').hasClass('button-hover')) {
          clearInterval(intervalId);
          intervalId = null;
          return;
        }
        
        // Checking of hover state from 
        // http://stackoverflow.com/a/8981521/2669960.
        var isHovered = !!$('.button').filter(function() {
          return $(this).is(":hover");
        }).length;
        
        if (isHovered) {
          console.log('Hover state is active');
        } else {
          console.log('Hover state is inactive');
          $('.button').addClass('button-hover');
          console.log('Added back the button-hover class');
          
          clearInterval(intervalId);
          intervalId = null;
        }
      }, 1000);
    }
    .button {
      color: green;
      border: none;
    }
    
    .button-hover:hover {
      background: yellow;
      border: none;
    }
    
    .button:active {
      border: none;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <button id='myButton' class='button button-hover'>Hello</button>

    编辑:我尝试的另一种方法是在 ontouchstart 或 ontouchend 中调用e.preventDefault()。当按钮被触摸时,它似乎停止了悬停效果,但它也停止了按钮单击动画,并防止在触摸按钮时调用 onclick 函数,因此您必须在 ontouchstart 或 ontouchend 处理程序中手动调用它们。不是一个非常干净的解决方案。

    【讨论】:

    • 谢谢,我没有使用基于间隔的方法,但您最后关于 preventDefault 的信息非常有帮助——我在其他任何地方都没有看到过这个帖子!
    【解决方案11】:

    这对我很有帮助:link

    function hoverTouchUnstick() {
        // Check if the device supports touch events
        if('ontouchstart' in document.documentElement) {
            // Loop through each stylesheet
            for(var sheetI = document.styleSheets.length - 1; sheetI >= 0; sheetI--) {
                var sheet = document.styleSheets[sheetI];
                // Verify if cssRules exists in sheet
                if(sheet.cssRules) {
                    // Loop through each rule in sheet
                    for(var ruleI = sheet.cssRules.length - 1; ruleI >= 0; ruleI--) {
                        var rule = sheet.cssRules[ruleI];
                        // Verify rule has selector text
                        if(rule.selectorText) {
                            // Replace hover psuedo-class with active psuedo-class
                            rule.selectorText = rule.selectorText.replace(":hover", ":active");
                        }
                    }
                }
            }
        }
    }
    

    【讨论】:

    • 您需要从全局 ontouchstart 事件处理程序中调用 hoverTouchUnstick() 一次。而且这个解决方案可以完美运行。
    【解决方案12】:

    将此JS代码添加到您的页面:

    document.body.className = 'ontouchstart' in document.documentElement ? '' : 'hover';
    

    现在在每次悬停之前的 CSS 中添加悬停类,如下所示:

    .hover .foo:hover {}
    

    如果设备是触摸的,则body类为空,否则其类为hover并应用规则!

    【讨论】:

    • 对我没用,但这样做了:document.body.className = 'ontouchstart' in window ? '' : 'hover';
    【解决方案13】:

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

    这是正确的起点。下一步: 在以下事件上应用/删除 nohover 类(使用 jQuery 演示)

    buttonelement
     .on("touchend touchcancel",function(){$(this).addClass("nohover")})
     .on("touchstart mouseover",function({$(this).removeClass("nohover")});   
    

    注意:如果您希望将其他类应用于按钮元素,CSS 中的 :not(.nohover) 将不再按预期工作。比您必须添加一个具有默认值和 !important 标记的单独定义来覆盖悬停样式: .nohover{ 背景颜色:白色 !important}

    这甚至可以正确处理 Chromebook Pixel(同时具有触摸屏和鼠标)之类的设备!而且我不认为这是一个主要的性能杀手......

    【讨论】:

      【解决方案14】:

      对我有用的解决方案:

      html {
         -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
      }
      

      将此代码添加到您的样式表中。

      我想摆脱单击链接时 iOS Safari 上出现的灰色背景。但它似乎做得更多。现在单击一个按钮(带有:hover 伪类!)立即打开!我只在 iPad 上测试过,我不知道它是否可以在其他设备上运行。

      【讨论】:

        【解决方案15】:

        我想分享一个很好的解决方案。 首先,您需要像这样检测用户是否在移动设备上:

        var touchDevice = /ipad|iphone|android|windows phone|blackberry/i.test(navigator.userAgent.toLowerCase());
        

        然后添加:

        if (!touchDevice) {
            $(".navbar-ul").addClass("hoverable");
        }
        

        在 CSS 中:

        .navbar-ul.hoverable li a:hover {
            color: #fff;
        }
        

        【讨论】:

        • 不适用于混合设备,例如Windows 触摸屏电脑。
        【解决方案16】:

        我遇到了类似的问题,我的应用程序兼容所有屏幕尺寸在 desktop-screen-size / 基于鼠标的设备中有很多悬停效果,后来我意识到基于触摸的设备导致了一种称为粘滞悬停的情况,这对于基于触摸的设备用户来说应用正常工作是一个障碍。

        我们在应用中使用了 SCSS。我定义了一个 mixin 来处理基于触摸的设备。

        @mixin hover-support {
          @media not all and (pointer: coarse) {
            &:hover {
              @content;
            }
          }
        }
        

        然后我将我所有的 css 类放在下面提到的 sn-p 下。

           @include hover-support() {
            // Your css-classes or css that you want to apply on those devices that support hover.
           }
        

        例如,我们有一个用于为图标设置动画的类,当我们将鼠标悬停在图标上时它会触发,正如您从 css 中看到的那样,但在基于触摸的设备中,它会受到粘性悬停效果的影响,然后我放置它在 @include hover-support() 内,以确保悬停仅适用于那些支持悬停的设备。

        @include hover-support() {
          .animate-icon {
            -webkit-transition: all 0.2s;
            transition: all 0.2s;
            &:hover {
              transform: scale(1.3);
              filter: brightness(85%);
              cursor: pointer;
            }
          }
        }
        

        【讨论】:

          【解决方案17】:

          这里有一些简单的 JavaScript 代码,不需要开发人员编辑 CSS 或编写任何新的 CSS 规则。我用 class="btn" 为 Bootstrap 按钮编写了这个,但它适用于任何具有特定类名的按钮。

          步骤如下:

          1. 确定这是否是触控设备
          2. 如果是,遍历document.styleSheets中的每一个CSS规则
          3. 删除任何同时包含.btn:hover 的规则

          消除所有.btn :hover CSS 规则确保按钮上不会出现视觉悬停效果。

          第 1 步:检测触控设备

          检查媒体查询是否存在(hover: none)

              const hasMatchMedia = typeof window.matchMedia !== 'undefined';
              /**
               * determine if device is touch-capable
               * true - device is touch-capable
               * false - device is not touch-capable
               * null - unable to determine touch capability
               * @return {null|boolean}
               */
              const hasTouch = () => {
                if (hasMatchMedia) {
                  return window.matchMedia('(hover: none)').matches;
                }
                return null;
              };
          

          第 2 步:删除包含 `btn` 和 `:hover` 的 CSS 规则

              /**
               * remove all CSS rules contaning both '.btn' and ':hover'
               * @return {number} count of rules deleted
               */
              function removeBtnHovers () {
          
                let rulesDeleted = 0;
          
                // recursively delete '.btn:hover' rules
                function recursiveDelete (rules, styleSheet) {
          
                  if (typeof rules === 'undefined' ||
                    typeof rules.length === 'undefined' ||
                    rules.length <= 0) {
                    return;
                  }
          
                  // iterate in reverse order,
                  // deleting any rule containing both '.btn' and ':hover'
                  const ruleLen = rules.length;
                  for (let i = ruleLen - 1; i >= 0; i--) {
                    const rule = rules[i];
                    if (typeof rule.cssRules === 'undefined') {
                      // a standard rule, evaluate it
                      const cssText = rule.cssText;
                      if (typeof cssText === 'string' &&
                        cssText.includes('.btn') &&
                        cssText.includes(':hover')) {
                        styleSheet.deleteRule(i);
                        rulesDeleted++;
                      }
                    } else {
                      // rule contains cssRules, iterate over them
                      recursiveDelete(rule.cssRules, rule);
                    }
                  }
                }
          
                // iterate over all style sheets in document
                for (const styleSheet of document.styleSheets) {
                  let rules = styleSheet.cssRules;
                  if (!rules) { continue; }
                  recursiveDelete(rules, styleSheet);
                }
                return rulesDeleted;
              }
          

          完整的代码在GitHubnpm

          terrymorse.com 的现场演示。

          【讨论】:

          • 这不是也消除了带有鼠标/触控板的触摸屏设备的悬停效果吗?
          • @Jensei 可能。如果设备匹配@media (hover:none),或者当用户第一次触摸屏幕时,悬停规则将被删除。您可以在现场演示页面上尝试一下。
          【解决方案18】:

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

          如果您通过onfocus/ontouch 设置背景颜色,则颜色样式会在:focus 状态消失后保持不变。
          您还需要重置 onblur 以在失去焦点时恢复默认背景。

          【讨论】:

          • 但是我想为鼠标用户保持悬停效果。
          • :hover 和 :active 可以接收相同的 CSS,它在 :focus 你有问题。实际上,如果您通过 onfocus 设置背景颜色,那么一旦焦点消失,颜色样式就会保持不变。您还需要重置 onlur 以恢复默认 bg
          【解决方案19】:

          这对我有用:将悬停样式放在一个新类中

          .fakehover {background: red}
          

          然后在需要时添加/删除类

          $(".someclass > li").on("mouseenter", function(e) {
            $(this).addClass("fakehover");
          });
          $(".someclass > li").on("mouseleave", function(e) {
            $(this).removeClass("fakehover");
          });
          

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

          【讨论】:

            【解决方案20】:

            我想我已经为类似问题找到了一个优雅的(最少的 js)解决方案:

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

            所以我只需将 this 处理程序附加到元素的 ontouchend 事件,如下所示:

            var unhover = function() {
              $("body").mousover();  
            };
            .hoverable {
              width: 100px;
              height: 100px;
              background: teal;
              cursor: pointer;
            }
            
            .hoverable:hover {
              background: pink;
            }
            &lt;div class="hoverable" ontouchend={unhover}&gt;&lt;/div&gt;

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

            【讨论】:

            • 对于您的代码 sn-p,我触摸后正方形仍然保持粉红色。我猜你并没有真正解决这个问题中提出的问题?
            • 此解决方案不起作用。首先,你使用了错误的方法来调用一个事件,你应该使用.trigger()。其次,无论哪种方式都不适用于移动 Safari。
            【解决方案21】:

            基于 Darren Cooks 的回答,如果您将手指移到另一个元素上,该回答也有效。

            Find element finger is on during a touchend event

            jQuery(function() {
                FastClick.attach(document.body);
            });
            // Prevent sticky hover effects for buttons on touch devices
            // From https://stackoverflow.com/a/17234319
            //
            //
            // Usage:
            // <a href="..." touch-focus-fix>..</a>
            //
            // Refactored from a directive for better performance and compability
            jQuery(document.documentElement).on('touchend', function(event) {
              'use strict';
            
              function fix(sourceElement) {
                var el = $(sourceElement).closest('[touch-focus-fix]')[0];
                if (!el) {
                  return;
                }
                var par = el.parentNode;
                var next = el.nextSibling;
                par.removeChild(el);
                par.insertBefore(el, next);
              }
            
              fix(event.target);
              var changedTouch = event.originalEvent.changedTouches[0];
              // http://www.w3.org/TR/2011/WD-touch-events-20110505/#the-touchend-event
              if (!changedTouch) {
                return;
              }
              var touchTarget = document.elementFromPoint(changedTouch.clientX, changedTouch.clientY);
              if (touchTarget && touchTarget !== event.target) {
                fix(touchTarget);
              }
            });
            

            Codepen Demo

            【讨论】:

              【解决方案22】:

              你可以试试这个方法。

              javascript:

              var isEventSupported = function (eventName, elementName) {
                  var el = elementName ? document.createElement(elementName) : window;
                  eventName = 'on' + eventName;
                  var isSupported = (eventName in el);
                  if (!isSupported && el.setAttribute) {
                      el.setAttribute(eventName, 'return;');
                      isSupported = typeof el[eventName] == 'function';
                  }
                  el = null;
                  return isSupported;
              };
              
              if (!isEventSupported('touchstart')) {
                  $('a').addClass('with-hover');
              }
              

              css:

              a.with-hover:hover {
                color: #fafafa;
              }
              

              【讨论】:

                【解决方案23】:

                到目前为止,我在项目中所做的是恢复触摸设备上的 :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;
                    }
                }
                

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

                【讨论】:

                  【解决方案24】:

                  这只需两步即可完美运行。

                  1. 将你的body标签设置为&lt;body ontouchstart=""&gt;。我不是这种“黑客”的粉丝,但它允许 iOS 上的 Safari 立即对触摸做出反应。不知道如何,但它有效。

                  2. 像这样设置你的可触摸类:

                    // I did this in SASS, but this should work with normal CSS as well
                    
                    // Touchable class
                    .example {
                    
                        // Default styles
                        background: green;
                    
                        // Default hover styles 
                        // (Think of this as Desktop and larger)
                        &:hover {
                            background: yellow;
                        }
                    
                        // Default active styles
                        &:active {
                            background: red;
                        }
                    
                        // Setup breakpoint for smaller device widths
                        @media only screen and (max-width: 1048px) {
                    
                            // Important!
                            // Reset touchable hover styles
                            // You may want to use the same exact styles as the Default styles
                            &:hover {
                                background: green;
                            }
                    
                            // Important!
                            // Touchable active styles
                            &:active {
                                background: red;
                            }
                        }
                    }
                    

                  您可能还想删除可触摸类上的任何动画。 Android Chrome 似乎比 iOS 慢一点。

                  如果用户在触摸您的类时滚动页面,这也会导致应用活动状态。

                  【讨论】:

                    【解决方案25】:

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

                    【讨论】:

                      【解决方案26】:

                      这可以通过交换一个 HTML 类来完成。与删除整个元素相比,它应该更不容易出现故障,尤其是对于大的图像链接等。

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

                      我们代码中唯一的重大变化是在实现新行为的元素上使用额外的 HTML 类,例如 &lt;a class='hover'&gt;&lt;/a&gt;

                      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

                      -

                      不过,我没有任何同时具备鼠标和触控功能的设备来对其进行正确测试。

                      【讨论】:

                        【解决方案27】:

                        使用 javascrpt 太容易了。那不是悬停问题,那是焦点问题。使用 css 聚焦时将轮廓设置为无。

                        .button:focus {
                        outline: none;
                        }    
                        

                        【讨论】:

                          【解决方案28】:

                          如果您是一名 CSS-in-JS 专家并正在寻找解决此问题的方法,那么这里就是。
                          您可以使用JS media queries 在 CSS-in-JS 中实现媒体查询。

                          例如下面的sn-p给按钮添加悬停效果,只有在屏幕尺寸大于768px的情况下。

                          tag: {
                            cursor: "pointer",
                            "&:hover, &:active": window.matchMedia('(min-width: 768px)').matches ? {
                              transform: "scale(1.3)"
                            } : null
                          }
                          

                          【讨论】:

                            【解决方案29】:

                            我的解决方案是在 touchend 之后克隆并替换节点...我讨厌这样做,但即使尝试使用 offsetHeight 重新绘制元素也不起作用

                                let cloneNode = originNode.cloneNode( true );
                                originNode.parentNode.replaceChild( cloneNode, originNode );
                            

                            【讨论】:

                            • 最后这对我不起作用......我的情况是我必须克隆一个扩展的整个 ES6 和几个事件侦听器,解决这样一个小问题变得越来越复杂.
                            【解决方案30】:

                            Kevin Lee's answer 在结尾处包含了一些我在其他任何地方都没有看到的内容,它帮助我解决了这个多输入系统(如触摸屏笔记本电脑)的问题,而且工作量很少。

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

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

                            唯一的缺点是您的 UI 框架中的任何其他点击功能(波纹动画等)都可能不会触发。您也许可以自己手动触发它们 - 在我的情况下,这首先不是问题。

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2012-02-16
                              • 1970-01-01
                              • 2013-07-11
                              • 2017-07-02
                              • 2013-10-22
                              • 2020-05-28
                              • 1970-01-01
                              相关资源
                              最近更新 更多