【问题标题】:Detect if a client device supports :hover and :focus states检测客户端设备是否支持 :hover 和 :focus 状态
【发布时间】:2019-03-15 11:56:24
【问题描述】:

听起来是一个简单的问题,但解决起来却非常具有挑战性。对于某些网站,我的内容只有在用户悬停/聚焦链接时才会显示。然而,链接本身有一个目标。

如果触摸屏用户单击其中一个链接,浏览器会立即转到href 位置。这意味着悬停内容永远不可见!

这就是为什么没有鼠标(或像魔术遥控器一样悬停的其他设备)的用户应该看到替代内容的原因。但是我该如何检测呢?

$(document).on('click','#my-menu-inner > ul > li > a',function(e) {

if(clientHasInputDeviceSupportingHover()) { 
  return true;
} else {
  e.preventDefault();
  $('#for-no-hover-visitors').html('');
  $(this).clone().appendTo('#for-no-hover-visitors');
  $(this).next().clone().appendTo('#for-no-hover-visitors');
}

});

function clientHasInputDeviceSupportingHover() {
    // HOW CAN I DETECT THIS???
    if($('#checkhover:checked').length > 0) {
      return true;
    }
    return false;
}
.clearfix::after {
    content: "";
    clear: both;
    display: table;
}

#my-menu-inner > ul {
  margin:10px;
  width:100%;
  background-color:yellow;
  list-style-type:none;
  position:relative;
}

#my-menu-inner > ul > li {
  float:left;
  margin:20px;
}

#my-menu-inner > ul > li > a {
  padding:20px;
  border:1px solid black;
  display:block;
}

#my-menu-inner > ul > li > div.sub {
   position:absolute;
   top:calc(100%  - 20px);
   background-color:red;
   padding:40px;
   display:none;
   left:0;
   width:100vw;
}

#my-menu-inner > ul > li a:hover + div.sub, #my-menu-inner > ul > li a:focus + div.sub,
#my-menu-inner > ul > li > div.sub:hover, #my-menu-inner > ul > li > div.sub:focus {
    display:block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Simulate for Client supporting hover: <input type="checkbox" id="checkhover" />

<div id="my-menu">
  <div id="my-menu-inner">
    <ul class="clearfix">
      <li>
        <a href="http://www.example.com/foo/">foo</a>
        <div class="sub">
          <ul>
            <li><a href="http://www.example.com/mobile/">mobile</a></li>
            <li><a href="http://www.example.com/users/">users</a></li>
          </ul>
        </div>
      </li>
      <li>
        <a href="http://www.example.com/bar/">bar</a>
        <div class="sub">
          <ul>
            <li><a href="http://www.example.com/never/">never</a></li>
            <li><a href="http://www.example.com/see-me/">see me</a></li>
          </ul>
        </div>
      </li>
    </ul>
  </div>
</div>

<div id="for-no-hover-visitors"></div>

问题是clientHasInputDeviceSupportingHover()找出这一点最可靠的方法是什么?

到目前为止我们所知道的

可以检测到触摸设备: What's the best way to detect a 'touch screen' device using JavaScript?

鼠标检测至少可以“onclick”工作: How to detect if a device has mouse support?

通常有很多不同的可能输入设备: https://en.wikipedia.org/wiki/Input_device#Pointing_device

非常欢迎通用/更可靠的解决方案。

【问题讨论】:

  • 您可以检查设备是否为手机。谷歌上有很多例子如何检查它的触摸设备。
  • @ConstantinChirila 这没用。例如,4K 触摸屏怎么样?带触摸板的迷你上网本怎么样?
  • 之前有人问过这个问题 - 例如在这里:stackoverflow.com/questions/21054126/… 不幸的是,虽然似乎没有好的解决方案 :( (我最近有完全相同的问题,最后只是测试了触摸支持 - 这并不理想,因为同时访问两者的用户体验更差,但我没有看到更好的东西。)
  • @RobinZigmond 鼠标支持的问题是相似的,因为我猜除了支持悬停/焦点的鼠标外,只有很少的输入可能性。但我希望可能有一个脚本可以模拟悬停或类似的东西并因此给出足够的答案?
  • 您可以在任何类型的设备上查看触控支持。可能我的评论让你失望了,因为我说的是移动。但正如@Robin Zigmond 所说,两种输入的用户体验更差。

标签: javascript jquery html css hover


【解决方案1】:

大多数设计师似乎有相反的问题,即摆脱烦人的双击来获取元素。

但是您可以尝试以下方法来解决问题并节省大量额外的代码或设备检测;

$("*").on("touchend", function(e) { $(this).hover(); });

注释

  • 您可以将 * 替换为特定的类或类型
  • 您可以向 touchend 添加事件,例如单击或鼠标悬停
  • 仅在 iphone XR ois 12.1 和 ipad ios 9.5、safari 和 chrome 上测试

【讨论】:

    【解决方案2】:

    W3C 似乎已经认识到了这个问题并引入了悬停功能:

    悬停媒体功能用于查询用户hover的能力 使用主要指针设备覆盖页面上的元素。如果一个 设备有多个指针设备,悬停媒体功能必须 反映“主要”指点设备的特性,如 由用户代理决定。 (查询任何的能力 可用的指点设备,请参阅任意悬停媒体功能。)

    甚至还有一个媒体查询来检查是否有可能悬停:

    any-pointerany-hover 媒体功能与 指针和悬停媒体特征,但它们对应于并集 用户可用的所有指点设备的功能。在里面 任意指针的情况下,可以匹配多个值,如果 不同的指点设备具有不同的特性。

    代码示例:

    /* Primary input mechanism system can 
       hover over elements with ease */
    @media (hover: hover) { ... }
    
    /* Primary input mechanism cannot hover 
       at all or cannot conveniently hover 
       (e.g., many mobile devices emulate hovering
       when the user performs an inconvenient long tap), 
       or there is no primary pointing input mechanism */
    @media (hover: none) { ... }
    
    /* One or more available input mechanism(s) 
       can hover over elements with ease */
    @media (any-hover: hover) { ... }
    
    
    /* One or more available input mechanism(s) cannot 
       hover (or there are no pointing input mechanisms) */
    @media (any-hover: none) { ... }
    

    正式稿:https://drafts.csswg.org/mediaqueries/#hover

    此功能仍然存在风险,但我真的希望它能够尽快得到全面支持,因为它已经被广泛支持:https://caniuse.com/#feat=css-media-interaction

    进一步阅读:https://css-tricks.com/touch-devices-not-judged-size/

    对于 Chrome 在这里测试您的设备:https://googlechrome.github.io/samples/media-hover-pointer/

    用 JavaScript 测试:https://jsfiddle.net/Blackbam/zkd2cs0t/16/

    目前最好的解决方案很可能是通过document.createEvent("TouchEvent"); 使用触摸检测和通过mousemove.hasMouse 进行鼠标检测的后备解决方案来使用这些媒体查询。

    【讨论】:

    • 这是一个不错的发现!
    • 请注意,IE11 不支持此功能。我会说谁在乎,但仅供参考。
    • 在 Firefox
    【解决方案3】:

    一种方法,如果您能够使用最新的 CSS(并检查链接资源中的兼容性表):

    /* Here we use media queries to test the device's
       support for the 'pointer' property, here we
       check if the 'fine' property-value is supported
       by the device: */
    @media (pointer:fine) {
       /* Here we set variables to use in styling
          the subsequent content which will identify
          the pointer support: */
       :root {
        --hasTouch: orangered;
      }
    }
    
    @media (pointer:coarse) {
       :root {
        --hasTouch: limegreen;
      }
    }
    
    @media (pointer:fine) and (pointer:coarse) {
       :root {
        --hasTouch: skyblue;
      }
    }
    
    /* Note that we don't specify the variable
       in the event of the device not supporting
       the 'pointer' property or property-value;
       therefore if the <div> is white (#fff) then
       we're using the default value from the
       var() function: */
    
    div {
      background-color: var(--hasTouch, #fff);
      height: 5em;
      border: 2px solid #000;
    }
    

    *,
     ::before,
     ::after {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    @media (pointer:fine) {
       :root {
        --hasTouch: orangered;
      }
    }
    
    @media (pointer:coarse) {
       :root {
        --hasTouch: limegreen;
      }
    }
    
    @media (pointer:fine) and (pointer:coarse) {
       :root {
        --hasTouch: skyblue;
      }
    }
    
    div {
      background-color: var(--hasTouch, #fff);
      height: 5em;
      border: 2px solid #000;
    }
    
    ul {
      width: 80vw;
      margin: 0.5em auto;
      list-style-type: none;
    }
    
    li::before {
      content: '';
      display: inline-block;
      width: 1em;
      height: 1em;
      background-color: currentColor;
      border-radius: 50%;
      border: 2px solid #000;
    }
    
    .failure {
      color: #000;
    }
    
    .orangered::before {
      color: orangered;
    }
    
    .limegreen::before {
      color: limegreen;
    }
    
    .skyblue::before {
      color: skyblue;
    }
    <div></div>
    <ul>
      <li class="failure"> - Doesn't appear to understand <code>pointer</code>.</li>
      <li class="orangered"> - Probably uses a mouse.</li>
      <li class="limegreen"> - Probably uses touch</li>
      <li class="skyblue"> - Could maybe have both mouse <em>and</em> touch.</li>
    </ul>

    JS Fiddle demo.

    参考资料:

    【讨论】:

    • 非常好的解释,它让我想到了一个更有希望的事情:@media (hover: hover) 。看我的回答。
    猜你喜欢
    • 1970-01-01
    • 2017-01-01
    • 2011-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多