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
像这样更新您的函数以使其正常工作:
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桌面版本中,
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); } |
在这里,他们使用的是非标准的
看看现代主义者是如何做到这一点的。
有关解释触摸检测问题的好文章,请参阅:斯图·考克斯:你无法检测到触摸屏。
更新:请阅读下面的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()); |
如果您使用现代化,如前所述,使用
但是,为了安全起见,我更喜欢使用
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 } |
如果不使用现代化,可以简单地用
另外请注意,测试用户代理
也看到这个问题
我们尝试了现代化的实现,但是检测触摸事件不再是一致的(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 } }); |
试图检测触摸的最大"抓取"是在支持触摸和触摸板/鼠标的混合设备上。即使您能够正确地检测到用户的设备是否支持触摸,您真正需要做的是检测用户当前使用的输入设备。这里有这个挑战的详细描述和可能的解决方案。
基本上,确定用户是刚触屏还是使用鼠标/触摸板的方法是在页面上同时注册
1 2 | document.addEventListener('touchstart', functionref, false) // on user tap,"touchstart" fires first document.addEventListener('mouseover', functionref, false) // followed by mouse event, ie:"mouseover" |
触摸操作将触发这两个事件,尽管前者(
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; } }); |
重要!
现在,您不需要在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 { } |
注意:还有其他选择器…
问题
由于混合设备使用触摸和鼠标输入的组合,因此您需要能够动态更改状态/变量,该状态/变量控制如果用户是触摸用户,是否应运行一段代码。
触控装置也可以轻点启动
在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
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) } |
快速提示那些可能想写
1 | undefined = window.ontouchstart; |
开发人员可以不定义他们想要的任何东西,您也可以检查
不要不尊重那些给出这个答案的人:我肯定我也在我的代码中写了这个;)
当连接鼠标时,可以假定用户在页面准备好后至少移动一小段距离(实际上是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 // } ); |