【问题标题】:Detect mouse on touch screen device在触摸屏设备上检测鼠标
【发布时间】:2015-02-18 21:39:10
【问题描述】:

我使用下面的代码来检测设备是否是触控设备:

var isTouchDevice = 'ontouchstart' in window || navigator.msMaxTouchPoints;

if(isTouchDevice)
{
    $('body').addClass('yes-touch');
}
else
{
    $('body').addClass('no-touch');
}

我使用它仅在它不是触摸设备时显示:hover 状态(因为大多数触摸设备将点击解释为悬停)。

.no-touch .element:hover {
    color: red;
}

问题是,我们办公室的一台 PC 是一体式触摸屏 PC,这意味着使用鼠标时不会出现悬停状态。

有没有办法判断鼠标是否在触摸屏设备上使用?换句话说,它应该在使用鼠标时应用no-touch 类,在使用触摸屏时应用yes-touch 类。

【问题讨论】:

    标签: javascript jquery touch


    【解决方案1】:

    截至今天,没有万无一失的铁定方法。现代人,几乎是特征检测方面的专家,最近有这样的说法:

    https://github.com/Modernizr/Modernizr/issues/869#issuecomment-57891034

    所有这一切的最终结果是您无法检测到鼠标在 一种符合 Modernizr 可靠性水平的方法 归功于。就我们的意图和目的而言,这是无法检测到的。

    如果您,未来的旅行者,希望尝试检测鼠标用户,那么 以下是我能提供的最佳指南。

    1. 不要。严重地。仅仅因为用户有“鼠标”并不意味着 他们没有多种其他形式的输入。你真的应该试试 很难避免做出任何基于变化的 UI/UX 决策 基于鼠标用户与鼠标用户截然相反的想法 触摸屏用户(或任何其他类型,就此而言)。做东西 通用。
    2. 如果你必须,并且只关心 IE 10 和 11,那么 IE 的 PointerEvent 值得一试。外部支持很糟糕 这两个(可能是未来的 IE 版本)中的一个。
    3. 您可以附加一个 监听身体上的“悬停”事件,如果它是真的,那么 用户可能有鼠标。这种方法的缺点包括 触摸事件在点击/触摸时短暂触发悬停事件,因此您可以 得到误报。
    4. 嗅探移动用户代理。这是一个坏主意, 并且违背了 Modernizr 的核心。请不要这样做。

    所以对我来说,#1 几乎可以概括。但是,这回答了您的问题,但没有给您解决方案。您提到“办公室中的我们的 台PC 之一......”这是偶然的仅限内部应用程序吗?我偶尔会遇到内部特殊用途或单页可能出于任何原因需要一些单独处理的情况(例如我们的一位员工使用带有鼠标的基于触摸的 AIO)。然后我要做的是将?hasmouse 附加到网址的末尾,并为用户提供指向书签的链接。然后在您的 var isTouchDevice 之后但在您的 if 之前的 javascript 中,插入此代码以撤消它:

    if (location.search == '?hasmouse') {
        isTouchDevice = false;
    }
    

    再说一次,这对于内部使用来说是一种朴实无华的方式。

    【讨论】:

    • 虽然这可能是现代化者的立场(我必须说有充分的理由),但这并不意味着永远没有需要它的用例。操作员问是否有办法......
    • @arctelix 不这样做的建议并不是说明没有可能的用例。这是因为目前(也许有一天)没有一种方法可以有效地在所有主要浏览器中有效地运行它。您发布的代码很好(而且很聪明),但是您依赖于
    • 我完全理解为什么现代人选择不这样做并且完全同意他们不应该这样做,但是发布的解决方案确实适用于那些希望生活在边缘的人。我理解所有关于可靠性的理论问题。但是,在所有支持触摸和鼠标的浏览器上,这对我来说都非常完美。理论上,结果可能不正确,但下一次触摸或鼠标移动会纠正它。我在带有触摸功能的 Windows 上开发,所以它一直在测试。我唯一担心的是,在某些情况下,这两个额外的事件侦听器是值得的。
    【解决方案2】:

    我已经使用了一段时间,它似乎可以可靠地工作。有时我会觉得值得,但它确实有效。

    这里的想法是捕获实际的触摸事件来触发触摸模式并使用 mousemove 来触发鼠标模式。问题是IE不会触发触摸事件,而是指针事件。指针事件的好处是您可以检查它是鼠标还是触摸!

    问题是所有其他浏览器都会在触摸事件之后触发假鼠标移动。真是让人抓狂!

    你可以看到它在这个codepen上工作

    //先判断这是不是触控设备:

    this.isTouch = 'ontouchstart' in window || (navigator.msMaxTouchPoints > 0);
    

    // 一些我们稍后需要的变量

    var lastTouch = 0
    var lastCheck = 0
    

    //然后设置我们的事件监听器:

    function initEvents() {
    
      //handle touch/mouse devices detect mouse so that touch is toggled off
      if (this.isTouch) {
    
        $(document).on(" touchstart mousemove " + msPointerEvent('move'), function(e) {
          e = e.originalEvent
            //browser has pointer events
          var pe = window.PointerEvent || window.MSPointerEvent
    
          // handle ie pointer events (polyfill functions are at bottom of answer)
    
          if (e.type == msPointerEvent('move')) {
            var touchEvent = msPointerType(e) == 'touch'
            if (touchEvent)
              lastTouch = e.timeStamp;
            if (!this.isTouch && touchEvent)
              return setupTouch.call(this, true)
            else if (this.isTouch && !touchEvent)
              return setupTouch.call(this, false)
          }
    
          // Handle all other browser touch events
          if (e.type == "touchstart") {
            console.log('touchstart fired')
            lastTouch = e.timeStamp;
            if (!this.isTouch)
              setupTouch.call(this, true);
          }
    
          // test mouse move and set up mouse mode if real
          else if (!pe && e.type == "mousemove" && this.isTouch) {
            if (realMouseDown.call(this, e)) {
              setupTouch.call(this, false)
            }
          }
        }.bind(this));
      }
    }
    initEvents()
    

    // 这是我们变得聪明的地方。事实证明,假的 mousemove 会在不到 500 毫秒的触摸内触发,所以我们用它来检测假货。然后当然要为 IE 做一些特别的事情:

    function realMouseDown(e) {
    
      var touchDif = e.timeStamp - lastTouch
      var mouseDif = e.timeStamp - lastCheck
    
      // false mouse event will get fired within 500ms of a touch (touchDif > 500)
      // (required for all browsers false mouse after touch event)
      var real = touchDif > 500
    
      lastCheck = e.timeStamp;
      console.log('real=', real, ' mDif ='+mouseDif, ' tDif ='+touchDif)
    
      return real
    }
    

    // 现在对于一些 IE polyfill,因为他们似乎无法决定要做什么。

    // IE pointer event polyfill
    function msPointerEvent(type) {
    
      var n = ""
    
      if (window.PointerEvent) // IE 11
        n = 'pointer' + type
      else if (window.MSPointerEvent) // IE 10
        n = 'MSPointer' + type[0].toUpperCase() + type.substr(1);
      return n
    }
    
    // IE pointer type polyfill
    function msPointerType(e) {
    
      var pt = ['zero', 'one', 'touch', 'pen', 'mouse']
    
      return typeof e.pointerType == 'string' ? e.pointerType : pt[e.pointerType]
    }
    

    // 最后做你需要的...

    // make required changes for touch / mouse
    var $output = $('#output')
    function setupTouch(state) {
      console.log('TouchMode=', state)
      if (state)
        this.isTouch = true
      else
        this.isTouch = false
      $output.html('Touch mode changed to = '+state)
    
    }
    

    //First check if this is a touch device:
    
    this.isTouch = 'ontouchstart' in window || (navigator.msMaxTouchPoints > 0);
    
    // Some vars we'll need later
    var lastTouch = 0
    var lastCheck = 0
    
    //Then set up our event listeners:
    
    function initEvents() {
    
      //handle touch/mouse devices detect mouse so that touch is toggled off
      if (this.isTouch) {
    
        $(document).on(" touchstart mousemove " + msPointerEvent('move'), function(e) {
          e = e.originalEvent
            //browser has pointer events
          var pe = window.PointerEvent || window.MSPointerEvent
    
          // handle ie pointer events (polyfill functions are at bottom of answer)
    
          if (e.type == msPointerEvent('move')) {
            var touchEvent = msPointerType(e) == 'touch'
            if (touchEvent)
              lastTouch = e.timeStamp;
            if (!this.isTouch && touchEvent)
              return setupTouch.call(this, true)
            else if (this.isTouch && !touchEvent)
              return setupTouch.call(this, false)
          }
    
          // Handle all other browser touch events
          else if (e.type == "touchstart") {
            console.log('touchstart fired')
            lastTouch = e.timeStamp;
            if (!this.isTouch)
              setupTouch.call(this, true);
          }
    
          // test mouse move and set up mouse mode if real
          else if (!pe && e.type == "mousemove" && this.isTouch) {
            if (realMouseDown.call(this, e)) {
              setupTouch.call(this, false)
            }
          }
        }.bind(this));
      }
    }
    initEvents()
      // Here is where we get clever. It turns out that the fake mousemove will fire in less than 500ms of the touch so we use that to detect fakes:
    
    function realMouseDown(e) {
    
      var touchDif = e.timeStamp - lastTouch
      var mouseDif = e.timeStamp - lastCheck
    
      // false mouse event will get fired within 500ms of a touch (touchDif > 500)
      // (required for all browsers false mouse after touch event)
      var real = touchDif > 500
    
      lastCheck = e.timeStamp;
      console.log('real=', real, ' mDif =' + mouseDif, ' tDif =' + touchDif)
    
      return real
    }
    
    // IE pointer event polyfill
    function msPointerEvent(type) {
    
      var n = ""
    
      if (window.PointerEvent) // IE 11
        n = 'pointer' + type
      else if (window.MSPointerEvent) // IE 10
        n = 'MSPointer' + type[0].toUpperCase() + type.substr(1);
      return n
    }
    
    // IE pointer type polyfill
    function msPointerType(e) {
    
      var pt = ['zero', 'one', 'touch', 'pen', 'mouse']
    
      return typeof e.pointerType == 'string' ? e.pointerType : pt[e.pointerType]
    }
    
    // make required changes for touch / mouse
    var $output = $('#output')
    
    
    function setupTouch(state) {
      console.log('TouchMode=', state)
      if (state) {
        this.isTouch = true
        $output.addClass('is-touch')
      } else {
        this.isTouch = false
        $output.removeClass('is-touch')
      }
      $output.html('Touch mode changed to = ' + state)
    
    }
    body {
      pointer-evetns: none;
    }
    #output.is-touch {
      background-color: blue;
      color: white;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div id="output">
      Touch or movethe mose on the result window to change the TouchMode state.
    </div>

    【讨论】:

      【解决方案3】:

      您可以检查附加到您的对象的指针事件的类型。

      请参阅下面的悬停示例:

      $('.element').on('pointerenter', function (e) {
          if (e.pointerType == 'mouse') {
              $(this).addClass('hover');
          }
      }).on('pointerleave', function (e) {
          if (e.pointerType == 'mouse') {
              $(this).removeClass('hover');
          }
      });
      

      并使用你的 CSS:

      .element.hover {
          color: red;
      }
      

      【讨论】:

      • e.pointerType 在 Firefox 81 Windows 10 Desktop 中为空。
      猜你喜欢
      • 2011-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-21
      • 1970-01-01
      • 2013-04-19
      相关资源
      最近更新 更多