检测客户端设备是否支持:hover和:focus状态

时间:2018-10-10 14:24:33

标签: javascript jquery html css hover

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

如果触摸屏用户单击了这些链接之一,则浏览器将立即转到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

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

3 个答案:

答案 0 :(得分:3)

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进行鼠标检测。

答案 1 :(得分:1)

一种方法,如果您能够使用最新的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

参考文献:

答案 2 :(得分:0)

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

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

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

笔记

  • 您可以将*替换为特定的类或类型
  • 您可以向触摸屏添加事件,例如单击或鼠标悬停
  • 仅在iPhone XR ois 12.1和ipad ios 9.5,Safari和Chrome上进行了测试