使用JavaScript检测"触摸屏"设备的最佳方法是什么?

scr*_*key 392 javascript jquery touch

我编写了一个jQuery插件,可以在桌面和移动设备上使用.我想知道是否有一种方法可以检测设备是否具有触摸屏功能.我正在使用jquery-mobile.js来检测触摸屏事件,它可以在iOS,Android等上运行,但我也想根据用户的设备是否有触摸屏来编写条件语句.

那可能吗?

bol*_*er2 634

你尝试过使用这个功能吗?(这与Modernizr过去常用的相同.)

function is_touch_device() {  
  try {  
    document.createEvent("TouchEvent");  
    return true;  
  } catch (e) {  
    return false;  
  }  
}

console.log(is_touch_device());
Run Code Online (Sandbox Code Playgroud)

更新1

document.createEvent("TouchEvent")已经开始回归true最新的铬(第17节).Modernizr不久前更新了这个.检查Modernizr测试在这里.

像这样更新你的功能,使其工作:

function is_touch_device1() {
  return 'ontouchstart' in window;
}

console.log(is_touch_device1());
Run Code Online (Sandbox Code Playgroud)

更新2

我发现以上不适用于IE10(在MS Surface上返回false).这是修复:

function is_touch_device2() {
  return 'ontouchstart' in window        // works on most browsers 
  || 'onmsgesturechange' in window;  // works on IE10 with some false positives
};

console.log(is_touch_device2());
Run Code Online (Sandbox Code Playgroud)

更新3

'onmsgesturechange' in window在某些IE桌面版本中将返回true,因此不可靠.这可以稍微更可靠地工作:

function is_touch_device3() {
  return !!('ontouchstart' in window        // works on most browsers 
  || navigator.maxTouchPoints);       // works on IE10/11 and Surface
};

console.log(is_touch_device3());
Run Code Online (Sandbox Code Playgroud)

更新2018年

时间流逝,有新的更好的方法来测试这个.我基本上提取并简化了Modernizr的检查方式:

function is_touch_device4() {
  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);
}

console.log(is_touch_device4());
Run Code Online (Sandbox Code Playgroud)

他们在这里使用的是非标准的touch-enabled媒体查询功能,我认为这有点奇怪和不好的做法.但是,嘿,在现实世界中,我猜它有效.将来(当所有人都支持它们时)这些媒体查询功能可以给你相同的结果:pointerhover.

查看Modernizr如何做到这一点来源.

有关解释触摸检测问题的优秀文章,请参阅: Stu Cox:您无法检测到触摸屏.

  • double bang将值转换为布尔值,强制函数返回"true"或"false".你可以在这里阅读更多相关信息:http://stackoverflow.com/questions/4686583/can-someone-explain-this-double-negative-trick (19认同)
  • 双重NOT(!!)是多余的,因为`in`运算符已经计算为布尔值. (17认同)
  • `'onmsgesturechange'`即使在非触摸设备(PC)中也是如此.`window.navigator.msMaxTouchPoints`似乎更准确.[在这里找到](http://blog.stevelydford.com/2012/03/detecting-touch-hardware-in-ie-10/). (11认同)
  • 即使我的屏幕没有触摸传感器,它也会在Windows 8上对IE10进行评估.我将旧笔记本电脑从Windows 7升级到Windows 8. (6认同)
  • 这不适用于Opera Mobile 10或Internet Explorer Mobile 6(Windows Mobile 6.5). (2认同)
  • 这篇文章的最新更新在IE10桌面窗口上返回true! (2认同)
  • 这个答案表明这是一个多少移动目标. (2认同)
  • 这不再起作用了。在最后一个 chrome 上它返回 true,即使我没有触摸设备 (2认同)
  • 太多的误报和例外都认为这是一个有效的答案. (2认同)

Ala*_*mas 138

更新:在将整个功能检测库拉入项目之前,请先阅读下面的blmstr答案.检测实际触摸支持更复杂,而Modernizr仅涵盖基本用例.

Modernizr是一种非常轻巧的方式,可以在任何网站上进行各种功能检测.

它只是为每个功能的html元素添加类.

然后,您可以在CSS和JS中轻松地定位这些功能.例如:

html.touch div {
    width: 480px;
}

html.no-touch div {
    width: auto;
}
Run Code Online (Sandbox Code Playgroud)

和Javascript(jQuery示例):

$('html.touch #popup').hide();
Run Code Online (Sandbox Code Playgroud)

  • Modernizr不测试触摸屏.它测试浏览器中是否存在触摸事件.请参阅文档中的"其他测试"部分:http://www.modernizr.com/docs/#features-misc (86认同)
  • 至于@ harrylove的观点,自从Windows 8问世以来,Modernizr一直错误地将我所有的PC浏览器都恢复为触控兼容. (7认同)
  • 更新:blmstr的答案更好,是一个纯粹的Javascript解决方案. (3认同)

Mat*_*tow 118

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

var supportsTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints;
Run Code Online (Sandbox Code Playgroud)

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

  • 这个答案应该被接受,因为它是最好的,也是最简单和最新的答案.在一个函数中,我相信这将更加一致:`return !!('ontouchstart'在窗口中)|| !!(在window.navigator中'msmaxtouchpoints');`(结合两个答案)在IE10中工作正常! (6认同)
  • 所有你需要的:`函数isTouchDevice(){返回'ontouchstart'在窗口中|| !!(navigator.msMaxTouchPoints);}` (6认同)
  • 窗口中的"onmsgesturechange"还会检测非触摸设备上的"桌面"IE10,因此这不是确定触摸的可靠方法. (3认同)

Dav*_*vid 36

使用上面的所有评论,我汇总了以下代码,以满足我的需求:

var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0));
Run Code Online (Sandbox Code Playgroud)

我在iPad,Android(浏览器和Chrome),Blackberry Playbook,iPhone 4s,Windows Phone 8,IE 10,IE 8,IE 10(带触摸屏的Windows 8),Opera,Chrome和Firefox上进行了测试.

它目前在Windows Phone 7上失败,但我还没有找到该浏览器的解决方案.

希望有人觉得这很有用.


Ben*_*uer 20

我喜欢这一个:

function isTouchDevice(){
    return typeof window.ontouchstart !== 'undefined';
}

alert(isTouchDevice());
Run Code Online (Sandbox Code Playgroud)

  • 不需要使用三元表达式来返回布尔值.只需使用表达式返回布尔值.`function isTouchDevice(){return(window.ontouchstart!== undefined); }` (12认同)
  • 正如之前接受的问题的评论中已经提到的那样."Modernizr不测试触摸屏.它测试浏览器中是否存在触摸事件".你的函数在技术上是hasTouchEvents()而不是isTouchDevice() (2认同)

小智 17

如果你使用Modernizr,它很容易使用Modernizr.touch,如前所述.

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

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
    }
Run Code Online (Sandbox Code Playgroud)

如果你不使用Modernizr,你可以简单地用Modernizr.touch上面的函数替换('ontouchstart' in document.documentElement)

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

另见这个问题

  • "做点敏感"和"不能碰这个"的奖励积分很多 (8认同)

Bla*_*bam 16

由于引入了交互媒体功能,您可以轻松做到:

if(window.matchMedia("(pointer: coarse)").matches) {
    // touchscreen
}
Run Code Online (Sandbox Code Playgroud)

https://www.w3.org/TR/mediaqueries-4/#descdef-media-any-pointer

更新(由于评论):上述解决方案是检测“粗指针”(通常是触摸屏)是否是主要输入设备。如果您想确定某个设备(例如鼠标)是否也具有触摸屏,则可以any-pointer: coarse改用它。

有关更多信息,请参见此处:检测浏览器没有鼠标并且仅是触摸式

  • 这是我测试过的唯一适用于所有情况的解决方案。谢谢! (5认同)
  • 这是迄今为止最好的解决方案,谢谢!尽管我建议使用`(pointer:粗糙)`,因为您很可能只针对主要输入。由于少数不支持的浏览器仅是台式机,因此可以在生产中使用。[css-tricks]上有一篇很棒的文章(https://css-tricks.com/touch-devices-not-judged-size/)。 (2认同)
  • 同意这可能是最好的解决方案,因为它利用了浏览器原生的触摸检测机制。我正在使用它,显然它效果非常好。 (2认同)

Mar*_*sch 14

我们尝试了modernizr实现,但检测到触摸事件不再一致(IE 10在Windows桌面上有触摸事件,IE 11可以工作,因为已经删除了触摸事件并添加了指针api).

因此,只要我们不知道用户的输入类型,我们就决定将网站优化为触摸网站.这比任何其他解决方案更可靠.

我们的研究表明,大多数桌面用户在点击之前用鼠标移动到屏幕上,因此我们可以在他们点击或悬停任何内容之前检测它们并改变行为.

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

var isTouch = true;
window.addEventListener('mousemove', function mouseMoveDetector() {
    isTouch = false;
    window.removeEventListener('mousemove', mouseMoveDetector);
});
Run Code Online (Sandbox Code Playgroud)

  • 触摸设备上的镀铬偶尔会触发鼠标移动 (3认同)
  • +1实际上这对我来说是最好的答案.例如,我有数据网格.这些数据网格将显示触摸编辑的"编辑图标"或更多选项的上下文菜单.如果检测到鼠标移动,则删除图标(节省网格空间),用户可以双击或右键单击. (2认同)

Aam*_*zad 9

工作小提琴

我这样做了;

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
}
Run Code Online (Sandbox Code Playgroud)


Iva*_*nos 9

有什么比检查他们是否有触摸屏更好,是检查他们是否使用它,加上更容易检查.

if (window.addEventListener) {
    var once = false;
    window.addEventListener('touchstart', function(){
        if (!once) {
            once = true;
            // Do what you need for touch-screens only
        }
    });
}
Run Code Online (Sandbox Code Playgroud)


Far*_*aei 7

我认为最好的方法是:

var isTouchDevice =
    (('ontouchstart' in window) ||
    (navigator.maxTouchPoints > 0) ||
    (navigator.msMaxTouchPoints > 0));
if(!isTouchDevice){
    /* Code for touch device /*
}else{
    /* Code for non touch device */
}
Run Code Online (Sandbox Code Playgroud)


Sat*_*thy 6

这个甚至在Windows Surface平板电脑上运行良好!

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");
}
}
Run Code Online (Sandbox Code Playgroud)


Saf*_*Ali 5

查看这篇文章,它提供了一个非常好的代码片段,说明检测到触摸设备时要执行的操作或调用 touchstart 事件时要执行的操作:

$(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();
}
Run Code Online (Sandbox Code Playgroud)


小智 5

我使用上面的代码片段来检测是否触摸,所以我的fancybox iframe 会显示在台式机上而不是触摸上。我注意到当单独使用 blmstr 的代码时,适用于 Android 4.0 的 Opera Mini 仍然注册为非触摸设备。(有谁知道为什么?)

我最终使用了:

<script>
$(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
    }
});
</script>
Run Code Online (Sandbox Code Playgroud)


hyb*_*rid 5

我会避免使用屏幕宽度来确定设备是否是触摸设备。有比 699px 大得多的触摸屏,想想 Windows 8。Navigatior.userAgent 可能很好地覆盖错误信息。

我建议您在 Modernizr 上查看这个问题

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


小智 5

其中许多都可以工作,但要么需要 jQuery,要么 javascript linter 抱怨语法。考虑到您最初的问题要求使用“JavaScript”(不是 jQuery,不是 Modernizr)方法来解决这个问题,这里有一个每次都有效的简单函数。它也是尽可能少的。

function isTouchDevice() {
    return !!window.ontouchstart;
}

console.log(isTouchDevice());
Run Code Online (Sandbox Code Playgroud)

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


Dav*_*urt 5

不,这是不可能的。给出的优秀答案只是片面的,因为任何给定的方法都会产生误报和漏报。由于操作系统 API,即使浏览器也不总是知道触摸屏是否存在,并且事实可能会在浏览器会话期间发生变化,尤其是在 KVM 类型的安排中。

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

http://www.stucox.com/blog/you-cant-detect-a-touchscreen/

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

文章的结论是:

对于布局,假设每个人都有触摸屏。鼠标用户使用大型 UI 控件比触摸用户使用小型 UI 控件更容易。悬停状态也是如此。

对于事件和交互,假设任何人都可能有触摸屏。实现键盘、鼠标和触摸交互,确保不会相互阻塞。


coc*_*ffs 5

尝试检测触摸的最大“问题”是在支持触摸和触控板/鼠标的混合设备上。即使您能够正确检测用户的设备是否支持触摸,您真正需要做的是检测用户当前使用的输入设备。此处详细记录了此挑战和可能的解决方案

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

document.addEventListener('touchstart', functionref, false) // on user tap, "touchstart" fires first
document.addEventListener('mouseover', functionref, false) // followed by mouse event, ie: "mouseover"
Run Code Online (Sandbox Code Playgroud)

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

;(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
})();
Run Code Online (Sandbox Code Playgroud)

更多细节在这里


Ame*_*icA 5

实际上,我研究了这个问题并考虑了所有情况。因为这也是我项目中的一个大问题。所以我达到了以下功能,它适用于所有设备上所有浏览器的所有版本:

const isTouchDevice = () => {
  const prefixes = ['', '-webkit-', '-moz-', '-o-', '-ms-', ''];
  const mq = query => window.matchMedia(query).matches;

  if (
    'ontouchstart' in window ||
    (window.DocumentTouch && document instanceof DocumentTouch)
  ) {
    return true;
  }
  return mq(['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join(''));
};
Run Code Online (Sandbox Code Playgroud)

提示:当然,isTouchDevice只是返回boolean值。