关于jquery:使用JavaScript检测“触摸屏”设备的最佳方法是什么?

What's the best way to detect a 'touch screen' device using JavaScript?

我已经编写了一个jquery插件,用于桌面和移动设备。我想知道是否有一种使用JavaScript的方法来检测设备是否具有触摸屏功能。我正在使用jquery-mobile.js检测触摸屏事件,它在iOS、Android等平台上工作,但我还想根据用户的设备是否有触摸屏来编写条件语句。

有可能吗?


你试过使用这个功能吗?(这和现代主义者过去使用的一样。)

1
2
3
4
5
6
7
8
function is_touch_device() {  
  try {  
    document.createEvent("TouchEvent");  
    return true;  
  } catch (e) {  
    return false;  
  }  
}

更新1

document.createEvent("TouchEvent")已经开始用最新的Chrome(第17版)返回true。不久前,现代主义者对此进行了更新。检查一下这里的现代化测试。

像这样更新您的函数以使其正常工作:

1
2
3
function is_touch_device() {
  return 'ontouchstart' in window;
}

更新2

我发现上面的方法不适用于IE10(在MS表面返回错误)。解决方法如下:

1
2
3
4
function is_touch_device() {
  return 'ontouchstart' in window        // works on most browsers
      || 'onmsgesturechange' in window;  // works on IE10 with some false positives
};

更新3

在某些IE桌面版本中,'onmsgesturechange' in window将返回true,因此这是不可靠的。这一点更可靠:

1
2
3
4
function is_touch_device() {
  return 'ontouchstart' in window        // works on most browsers
      || navigator.maxTouchPoints;       // works on IE10/11 and Surface
};

更新2018

时间一天天过去,有新的更好的方法来测试这一点。我基本上提取并简化了现代主义的检查方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function is_touch_device() {
  var prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');
  var mq = function(query) {
    return window.matchMedia(query).matches;
  }

  if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
    return true;
  }

  // include the 'heartz' as a way to have a non matching MQ to help terminate the join
  // https://git.io/vznFH
  var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');
  return mq(query);
}

在这里,他们使用的是非标准的touch-enabled媒体查询功能,我认为这是一种奇怪和糟糕的做法。但是,在现实世界里,我想这是可行的。将来(当所有的媒体都支持它们时),这些媒体查询功能可以为您提供相同的结果:pointerhover

看看现代主义者是如何做到这一点的。

有关解释触摸检测问题的好文章,请参阅:斯图·考克斯:你无法检测到触摸屏。


更新:请阅读下面的BLMSTR答案,然后将整个功能检测库拉入您的项目。检测实际的触摸支持更为复杂,而Modernizer只包含一个基本的用例。

Modernizer是在任何站点上进行各种功能检测的一种非常好的、轻量级的方法。

它只是为每个特性向HTML元素添加类。

然后,您可以在CSS和JS中轻松地将这些特性作为目标。例如:

1
2
3
4
5
6
7
html.touch div {
    width: 480px;
}

html.no-touch div {
    width: auto;
}

和javascript(jquery示例):

1
$('html.touch #popup').hide();


由于Modernizer在Windows Phone 8/WinRT上没有检测到IE10,一个简单的跨浏览器解决方案是:

1
var supportsTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints;

您只需要检查一次,因为设备不会突然支持或不支持触摸,所以只需将其存储在变量中,这样您就可以更有效地多次使用它。


使用上面的所有注释,我已经组装了满足我需要的以下代码:

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

我在iPad、Android(浏览器和Chrome)、BlackBerry Playbook、iPhone 4S、Windows Phone 8、IE 10、IE 8、IE 10(带触摸屏的Windows 8)、Opera、Chrome和Firefox上测试过。

它目前在WindowsPhone7上失败,我还没有找到该浏览器的解决方案。

希望有人能发现这一点。


我喜欢这个:

1
2
3
4
5
function isTouchDevice(){
    return typeof window.ontouchstart !== 'undefined';
}

alert(isTouchDevice());


如果您使用现代化,如前所述,使用Modernizr.touch非常容易。

但是,为了安全起见,我更喜欢使用Modernizr.touch和用户代理测试的组合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var deviceAgent = navigator.userAgent.toLowerCase();

var isTouchDevice = Modernizr.touch ||
(deviceAgent.match(/(iphone|ipod|ipad)/) ||
deviceAgent.match(/(android)/)  ||
deviceAgent.match(/(iemobile)/) ||
deviceAgent.match(/iphone/i) ||
deviceAgent.match(/ipad/i) ||
deviceAgent.match(/ipod/i) ||
deviceAgent.match(/blackberry/i) ||
deviceAgent.match(/bada/i));

if (isTouchDevice) {
        //Do something touchy
    } else {
        //Can't touch this
    }

如果不使用现代化,可以简单地用('ontouchstart' in document.documentElement)替换上面的Modernizr.touch功能。

另外请注意,测试用户代理iemobile将比Windows Phone提供更广泛的检测到的Microsoft移动设备范围。

也看到这个问题


我们尝试了现代化的实现,但是检测触摸事件不再是一致的(IE10在Windows桌面上有触摸事件,IE11工作,因为已经删除了触摸事件并添加了指针API)。

所以我们决定将网站优化为一个触摸网站,只要我们不知道用户有什么输入类型。这比任何其他解决方案都更可靠。

我们的研究表明,大多数桌面用户在点击之前都会将鼠标放在屏幕上移动,因此我们可以在他们能够点击或悬停任何东西之前检测到他们并改变他们的行为。

这是我们代码的简化版本:

1
2
3
4
5
var isTouch = true;
window.addEventListener('mousemove', function mouseMoveDetector() {
    isTouch = false;
    window.removeEventListener('mousemove', mouseMoveDetector);
});


有比检查他们是否有触摸屏更好的方法,就是检查他们是否在使用触摸屏,而且更容易检查。

1
2
3
4
5
6
7
8
9
if (window.addEventListener) {
    var once = false;
    window.addEventListener('touchstart', function(){
        if (!once) {
            once = true;
            // Do what you need for touch-screens only
        }
    });
}

工作小提琴

我是这样做到的;

1
2
3
4
5
6
7
8
9
10
function isTouchDevice(){
    return true == ("ontouchstart" in window || window.DocumentTouch && document instanceof DocumentTouch);
}

if(isTouchDevice()===true) {
    alert('Touch Device'); //your logic for touch device
}
else {
    alert('Not a Touch Device'); //your logic for non touch device
}


这个即使在Windows平板电脑上也能很好地工作!!!!

1
2
3
4
5
6
7
8
9
10
function detectTouchSupport {
msGesture = window.navigator && window.navigator.msPointerEnabled && window.MSGesture,
touchSupport = (("ontouchstart" in window ) || msGesture || window.DocumentTouch &&     document instanceof DocumentTouch);
if(touchSupport) {
    $("html").addClass("ci_touch");
}
else {
    $("html").addClass("ci_no_touch");
}
}

由于引入了交互媒体功能,您只需执行以下操作:

1
2
3
if(window.matchMedia("(any-pointer: coarse)").matches) {
    // touchscreen
}

https://www.w3.org/tr/mediaqueries-4/descdef media任意指针


我使用上面的代码片段来检测是否触摸,所以我的FancyBox iframes会出现在台式电脑上,而不是在触摸上。我注意到,仅在使用BLMSTR的代码时,Android4.0版的Operamini仍在注册为非触摸设备。(有人知道为什么吗?)

我最终使用了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$(document).ready(function() {
    var ua = navigator.userAgent;
    function is_touch_device() {
        try {  
            document.createEvent("TouchEvent");  
            return true;  
        } catch (e) {  
            return false;  
        }  
    }

    if ((is_touch_device()) || ua.match(/(iPhone|iPod|iPad)/)
    || ua.match(/BlackBerry/) || ua.match(/Android/)) {
        // Touch browser
    } else {
        // Lightbox code
    }
});


试图检测触摸的最大"抓取"是在支持触摸和触摸板/鼠标的混合设备上。即使您能够正确地检测到用户的设备是否支持触摸,您真正需要做的是检测用户当前使用的输入设备。这里有这个挑战的详细描述和可能的解决方案。

基本上,确定用户是刚触屏还是使用鼠标/触摸板的方法是在页面上同时注册touchstartmouseover事件:

1
2
document.addEventListener('touchstart', functionref, false) // on user tap,"touchstart" fires first
document.addEventListener('mouseover', functionref, false) // followed by mouse event, ie:"mouseover"

触摸操作将触发这两个事件,尽管前者(touchstart)在大多数设备上始终是第一个。因此,基于可预测的事件序列,您可以创建一种机制,动态地向文档根目录添加或删除can-touch类,以反映当前用户在文档上的输入类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
;(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
})();

更多详细信息。


不,不可能。给出的优秀答案只是部分的,因为任何给定的方法都会产生假阳性和假阴性。由于操作系统API的存在,即使是浏览器也不总是知道触摸屏是否存在,而且在浏览器会话期间,事实可能会发生变化,特别是在KVM类型的安排下。

请参阅这篇优秀文章中的更多详细信息:

http://www.stucox.com/blog/you-cant-detect-a-触摸屏/

这篇文章建议你重新考虑那些让你想要检测触摸屏的假设,它们可能是错误的。(我检查了自己的应用程序,我的假设确实是错误的!)

文章总结如下:

For layouts, assume everyone has a touchscreen. Mouse users can use
large UI controls much more easily than touch users can use small
ones. The same goes for hover states.

For events and interactions, assume anyone may have a touchscreen.
Implement keyboard, mouse and touch interactions alongside each other,
ensuring none block each other.


我会避免使用屏幕宽度来确定设备是否是触摸设备。有比699px大得多的触摸屏,想想Windows8。navigatior.useragent可以很好地覆盖误报。

我建议你去看看关于现代化的这个问题。

您想测试设备是否支持触摸事件还是触摸设备。不幸的是,这不是同一件事。


看看这篇文章,它给出了一个非常好的代码片段,说明在检测到触摸设备时要做什么,或者如果调用了TouchStart事件,该怎么做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$(function(){
  if(window.Touch) {
    touch_detect.auto_detected();
  } else {
    document.ontouchstart = touch_detect.surface;
  }
}); // End loaded jQuery
var touch_detect = {
  auto_detected: function(event){
    /* add everything you want to do onLoad here (eg. activating hover controls) */
    alert('this was auto detected');
    activateTouchArea();
  },
  surface: function(event){
    /* add everything you want to do ontouchstart here (eg. drag & drop) - you can fire this in both places */
    alert('this was detected by touching');
    activateTouchArea();
  }
}; // touch_detect
function activateTouchArea(){
  /* make sure our screen doesn't scroll when we move the"touchable area" */
  var element = document.getElementById('element_id');
  element.addEventListener("touchstart", touchStart, false);
}
function touchStart(event) {
  /* modularize preventing the default behavior so we can use it again */
  event.preventDefault();
}

其中许多都可以工作,但要么需要jquery,要么JavaScriptLinters抱怨语法。考虑到您最初的问题需要一种"javascript"(不是jquery,不是modernizer)方法来解决这个问题,这里有一个每次都有效的简单函数。它也尽可能的少。

1
2
3
4
5
function isTouchDevice() {
    return !!window.ontouchstart;
}

console.log(isTouchDevice());

我最后要提到的一个好处是,这个代码是框架和设备无关的。享受!


看起来Chrome24现在支持触摸事件,可能适用于Windows8。所以这里发布的代码不再有效。我现在绑定touch和click事件,并确保只调用一个事件,而不是试图检测浏览器是否支持touch:

1
2
3
4
5
6
7
8
9
myCustomBind = function(controlName, callback) {

  $(controlName).bind('touchend click', function(e) {
    e.stopPropagation();
    e.preventDefault();

    callback.call();
  });
};

然后称之为:

1
myCustomBind('#mnuRealtime', function () { ... });

希望这有帮助!


您可以安装Moderner并使用简单的触摸事件。这非常有效,适用于我测试过的所有设备,包括Windows Surface!

我已经创建了一个jsiddle

1
2
3
4
5
6
7
8
9
function isTouchDevice(){
    if(Modernizr.hasEvent('touchstart') || navigator.userAgent.search(/Touch/i) != -1){
         alert("is touch");
            return true;
         }else{
            alert("is not touch");
            return false;
    }
}


我使用:

1
2
3
if(jQuery.support.touch){
    alert('Touch enabled');
}

在jquery Mobile 1.0.1中


1
var isTouchScreen = 'createTouch' in document;

1
2
3
var isTouchScreen = 'createTouch' in document || screen.width <= 699 ||
    ua.match(/(iPhone|iPod|iPad)/) || ua.match(/BlackBerry/) ||
    ua.match(/Android/);

我想是更彻底的检查。


在如何在javascript中检测页面是否显示在触摸屏设备上这一问题上,我也遇到了很多困难。到目前为止,没有真正的选择来正确地检测这个选择。浏览器要么在台式机上报告触摸事件(因为操作系统可能已准备好触摸),要么一些解决方案无法在所有移动设备上工作。

最后,我意识到我从一开始就走错了路:如果我的页面在触摸和非触摸设备上看起来很相似,我可能根本不必担心检测属性:我的场景是关闭触摸设备上按钮的工具提示,因为它们会导致双击,而我只需点击一次按钮即可激活按钮。

我的解决方案是重构视图,这样一个按钮上就不需要工具提示,最后,我不需要用所有有缺点的方法从javascript中检测触摸设备。


实际的答案似乎是考虑到环境:

1)公共站点(不登录)对用户界面进行编码,以便同时使用这两个选项。

2)登录网站捕获登录窗体上是否发生鼠标移动,并将其保存到隐藏输入中。该值与登录凭据一起传递并添加到用户的会话中,因此可以在会话期间使用。

仅添加到登录页面的jquery:

1
2
3
4
5
6
7
8
9
$('#istouch').val(1); // <-- value will be submitted with login form

if (window.addEventListener) {
    window.addEventListener('mousemove', function mouseMoveListener(){
        // Update hidden input value to false, and stop listening
        $('#istouch').val(0);
        window.removeEventListener('mousemove', mouseMoveListener);
    });
}

(+1到@Dave Burt,+1到@Martin Lantzsch,关于他们的答案)


JQuery V1.11

在提供的答案中有很多好的信息。但是,最近我花了很多时间试图把所有的事情联系在一起,形成一个有效的解决方案,来完成两件事:

  • 检测正在使用的设备是否为触摸屏类型的设备。
  • 检测设备是否被攻丝。
  • 除了这篇文章和用javascript检测触摸屏设备,我发现Patrick Lauke的这篇文章非常有用:https://hacks.mozilla.org/2013/04/detecting-touch-its-the-why-not-the-how/

    这是密码……

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    $(document).ready(function() {
    //The page is"ready" and the document can be manipulated.

        if (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0))
        {
          //If the device is a touch capable device, then...
          $(document).on("touchstart","a", function() {

            //Do something on tap.

          });
        }
        else
        {
          null;
        }
    });

    重要!*.on( events [, selector ] [, data ], handler )方法需要有一个选择器,通常是一个元素,可以处理"touchStart"事件或与touch相关的任何其他类似事件。在这种情况下,它是超链接元素"a"。

    现在,您不需要在javascript中处理常规的鼠标点击,因为您可以使用css来处理这些事件,使用选择器来处理超链接"a"元素,如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    /* unvisited link */
    a:link
    {

    }

    /* visited link */
    a:visited
    {

    }

    /* mouse over link */
    a:hover
    {

    }

    /* selected link */
    a:active
    {

    }

    注意:还有其他选择器…


    问题

    由于混合设备使用触摸和鼠标输入的组合,因此您需要能够动态更改状态/变量,该状态/变量控制如果用户是触摸用户,是否应运行一段代码。

    触控装置也可以轻点启动mousemove

    解决方案

  • 假设加载时触摸为假。
  • 等待直到触发touchstart事件,然后将其设置为true。
  • 如果触发了TouchStart,请添加一个mousemove处理程序。
  • 如果两个mousemove事件之间的触发时间小于20毫秒,则假定它们使用鼠标作为输入。删除不再需要的事件,mousemove对于鼠标设备来说是一个昂贵的事件。
  • 一旦touchStart再次启动(用户返回到使用touch),变量就会设置回true。重复这个过程,以动态的方式确定。如果神奇的mousemove在触摸时被触发了两次(在我的测试中,几乎不可能在20毫秒内完成),那么下一个touchstart会将其设置为真。
  • 在Safari iOS和Android版Chrome上测试。

    注意:对于MS Surface等的指针事件,不能100%确定。

    科德森演示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    const supportsTouch = 'ontouchstart' in window;
    let isUsingTouch = false;

    // `touchstart`, `pointerdown`
    const touchHandler = () => {
      isUsingTouch = true;
      document.addEventListener('mousemove', mousemoveHandler);
    };

    // use a simple closure to store previous time as internal state
    const mousemoveHandler = (() => {
      let time;

      return () => {
        const now = performance.now();

        if (now - time < 20) {
          isUsingTouch = false;
          document.removeEventListener('mousemove', mousemoveHandler);
        }

        time = now;
      }
    })();

    // add listeners
    if (supportsTouch) {
      document.addEventListener('touchstart', touchHandler);
    } else if (navigator.maxTouchPoints || navigator.msMaxTouchPoints) {
      document.addEventListener('pointerdown', touchHandler);
    }

    除了桌面版的firefox之外,所有支持的浏览器都是真的,因为桌面版的firefox支持开发人员的响应式设计,即使你点击或不点击按钮!

    我希望Mozilla能在下一个版本中解决这个问题。

    我正在使用火狐28桌面。

    1
    2
    3
    4
    function isTouch()
    {
        return !!("ontouchstart" in window) || !!(navigator.msMaxTouchPoints);
    }


    扩展jquery support对象:

    1
    jQuery.support.touch = 'ontouchend' in document;

    现在你可以在任何地方检查它,像这样:

    1
    2
    if( jQuery.support.touch )
       // do touch stuff

    到目前为止,这似乎对我很有效:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //Checks if a touch screen
    is_touch_screen = 'ontouchstart' in document.documentElement;

    if (is_touch_screen) {
      // Do something if a touch screen
    }
    else {
      // Not a touch screen (i.e. desktop)
    }

    快速提示那些可能想写window.ontouchstart !== undefined->的人,请使用micnic答案,因为undefined不是javascript中的关键字。如果develop写的是这样的东西,那就大问题了:

    1
    undefined = window.ontouchstart;

    开发人员可以不定义他们想要的任何东西,您也可以检查window.ontouchstart = myNonExistingWord;

    不要不尊重那些给出这个答案的人:我肯定我也在我的代码中写了这个;)


    当连接鼠标时,可以假定用户在页面准备好后至少移动一小段距离(实际上是100%),而无需点击。下面的机制检测到这一点。如果检测到,我认为这是缺少触摸支持的迹象,或者,如果支持,在使用鼠标时,这一点无关紧要。如果未检测到,则假定为触摸设备。

    编辑此方法可能不适用于所有目的。它可以用于控制基于加载页面上的用户交互而激活的功能,例如图像查看器。下面的代码也会将mousemove事件绑定到没有鼠标的设备上,因为它现在很突出。其他方法可能更好。

    大致上是这样的(对jquery很抱歉,但在纯JavaScript中类似):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    var mousedown, first, second = false;
    var ticks = 10;
    $(document).on('mousemove', (function(e) {
        if(UI.mousechecked) return;
        if(!first) {
            first = e.pageX;
            return;
            }
        if(!second && ticks-- === 0) {
            second = e.pageX;
            $(document).off('mousemove'); // or bind it to somewhat else
            }
        if(first  && second  && first !== second && !mousedown){
            // set whatever flags you want
            UI.hasmouse = true;
            UI.touch = false;
            UI.mousechecked = true;
        }

        return;
    }));
    $(document).one('mousedown', (function(e) {
        mousedown = true;
        return;
    }));
    $(document).one('mouseup', (function(e) {
        mousedown = false;
        return;
    }));

    您可以使用以下代码:

    1
    2
    3
    4
    5
    function isTouchDevice() {
       var el = document.createElement('div');
       el.setAttribute('ongesturestart', 'return;'); // or try"ontouchstart"
       return typeof el.ongesturestart ==="function";
    }

    来源:正在检测基于触摸的浏览和@mplungjan post。

    上述解决方案基于检测事件支持而不使用浏览器嗅探文章。

    您可以在下面的测试页上检查结果。

    请注意,以上代码仅测试浏览器是否支持触摸,而不是设备。因此,如果您的笔记本电脑带有触摸屏,您的浏览器可能不支持触摸事件。最近的Chrome支持触摸事件,但其他浏览器可能不支持。

    您也可以尝试:

    1
    2
    3
    if (document.documentElement.ontouchmove) {
      // ...
    }

    但它可能无法在iPhone设备上工作。


    1
    $.support.touch ?"true" :"false";

    如果您测试touchStart,那么在document.documentElement中是受支持的,这非常简单。

    1
    2
    3
    4
    var x = 'touchstart' in document.documentElement;
    console.log(x)
    // return true if is supported
    // else return false


    虽然它只在alpha中,但jquery移动框架值得一看。它将在移动浏览器中规范化这些类型的事件。也许看看他们在做什么。我假设jquery-mobile.js与此框架有所不同。


    这种方法对我很有效,它等待第一次用户交互以确保他们在触摸设备上。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var touchEnabled = false;
    $(document.body).one('touchstart',
        function(e){
            touchEnabled=true;
            $(document.documentElement).addClass("touch");
            // other touch related init
            //
        }
    );