Detect mouse on touch screen device

拈花ヽ惹草 提交于 2020-12-04 08:24:38

问题


I use the following code to detect whether the device is a touch device or not:

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

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

I use this to only show :hover states when it is NOT a touch device (as most touch devices interpret a tap as a hover).

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

The problem is, one of our PCs in the office is an all-on-one touch screen PC, which means that when using a mouse the hover states don't occur.

Is there a way to work out whether a mouse is being used on a touch screen device? In other words, it should have the no-touch class applied when the mouse is being used and the yes-touch class applied when the touch screen is being used.


回答1:


As of today, there is no foolproof ironclad way of doing it. The modernizr folks, pretty much the experts in feature detection, recently had this to say about it:

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

The end result of all of this is that you cannot detect a mouse use in a way that would conform to the level of reliability that Modernizr is credited with. For our intents and purposes, it is a undetectable.

If you, future traveler, wish to attempt to detect a mouse user, then the following is the best guide I can offer.

  1. Don't. Seriously. Just because a user has a "mouse" doesn't mean that they don't have multiple other forms of input. You should try really hard to avoid making any kind of UI/UX decision that changes based upon the idea of a mouse user being diametrically opposed to a touchscreen user (or any other kind, for that matter). Make things universal.
  2. If you have to, and only care about IE 10 and 11, then IE's PointerEvent would be worth checking out. Support is abysmal, outside of those two (and presumably future IE versions).
  3. You can attach a listener for a 'hover' event on the body, and if it is true, then the user probably has a mouse. The drawback with this approach include touch events briefly firing hover events on tap/touch, so you could get false positives.
  4. sniff for mobile user agents. This is a bad idea, and goes against the very core of Modernizr. Please don't do it.

So to me #1 pretty much sums it up. However, that answers your question but doesn't give you a solution. You mention "one of our PC's in the office..." Is this by chance an internal only application? I've occasionally run across situations where internal special use or one off pages may require some individual treatment for whatever reason (like one of our employees having a touch based AIO with a mouse attached). What I'll do then is append a ?hasmouse onto the end of the url and give the user that link to bookmark. Then inside javascript after your var isTouchDevice but before your if, insert this code to undo it:

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

Again, thats sort of a no frills way for just internal use.




回答2:


I have been using this for a while and it seems to work reliably. I wounder if it's worth it sometimes, but it does work.

The idea here is to capture actual touchdown events to trigger touch mode and use mousemove to trigger mouse mode. The problem is IE does not trigger touch events, but pointer events. The great thing about pointer events is you can check if it's mouse or touch!

The problem is all other browsers fire a fake mousemove just after a touch event. It's truly maddening!

You can see it work on this codepen

//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
      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. Then of course do something special for 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
}

// Now for some IE polyfill because they cant seem to make up their mind what to do.

// 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]
}

// And finally do what you need...

// 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:


You can check for type of Pointer Event, that attached to Your object.

Please see example for hover below:

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

And use your css:

.element.hover {
    color: red;
}


来源:https://stackoverflow.com/questions/28594445/detect-mouse-on-touch-screen-device

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!