我已经编写了可在台式机和移动设备上使用的jQuery插件。我想知道JavaScript是否可以检测设备是否具有触摸屏功能。我正在使用jquery-mobile.js来检测触摸屏事件,它可以在iOS,Android等系统上运行,但是我还想根据用户设备是否具有触摸屏来编写条件语句。
那可能吗?
我已经编写了可在台式机和移动设备上使用的jQuery插件。我想知道JavaScript是否可以检测设备是否具有触摸屏功能。我正在使用jquery-mobile.js来检测触摸屏事件,它可以在iOS,Android等系统上运行,但是我还想根据用户设备是否具有触摸屏来编写条件语句。
那可能吗?
Answers:
更新:在将整个功能检测库引入项目之前,请阅读下面的blmstr答案。检测实际的触摸支持更为复杂,而Modernizr仅涵盖一个基本用例。
Modernizr是一种出色的轻量级方法,可在任何站点上执行各种功能检测。
它只是为每个功能将类添加到html元素。
然后,您可以在CSS和JS中轻松定位这些功能。例如:
html.touch div {
width: 480px;
}
html.no-touch div {
width: auto;
}
和Javascript(jQuery示例):
$('html.touch #popup').hide();
Modernizr.touch
意外返回undefined
,则可能有一个自定义的Modernizr(您可以选择)而不支持触摸事件检测。
您是否尝试过使用此功能?(这与Modernizr过去使用的相同。)
function is_touch_device() {
try {
document.createEvent("TouchEvent");
return true;
} catch (e) {
return false;
}
}
console.log(is_touch_device());
更新1
document.createEvent("TouchEvent")
已开始返回true
最新版本的Chrome(第17版)。Modernizr前不久对此进行了更新。在此处检查Modernizr测试。
像这样更新您的功能以使其工作:
function is_touch_device1() {
return 'ontouchstart' in window;
}
console.log(is_touch_device1());
更新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());
更新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());
更新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());
他们在这里使用的是非标准touch-enabled
媒体查询功能,我认为这是一种怪异和不道德的做法。但是,嘿,在现实世界中,我认为它是可行的。将来(当所有人都支持它们时),这些媒体查询功能可以为您提供相同的结果:pointer
和hover
。
有关解释触摸检测问题的好文章,请参阅: Stu Cox:您无法检测到触摸屏。
in
运算符已经计算出一个布尔值。
由于Modernizr在Windows Phone 8 / WinRT上未检测到IE10,因此一个简单的跨浏览器解决方案是:
var supportsTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints;
您只需要检查一次即可,因为设备不会突然支持或不支持触摸,因此只需将其存储在变量中,便可以更有效地使用它多次。
return !!('ontouchstart' in window) || !!('msmaxtouchpoints' in window.navigator);
结合两个答案)在IE10中也能正常工作!
'onmsgesturechange' in window
即使可以触摸,在IE10桌面上也返回true
function isTouchDevice() { return 'ontouchstart' in window || !!(navigator.msMaxTouchPoints);}
使用上面的所有注释,我汇编了以下符合我的需要的代码:
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上对此进行了测试。
目前,它在Windows Phone 7上无法正常运行,我还无法为该浏览器找到解决方案。
希望有人觉得这有用。
true
上的窗户上我的Firefox 32 7 :(
由于引入了交互媒体功能,您可以轻松做到:
if(window.matchMedia("(pointer: coarse)").matches) {
// touchscreen
}
https://www.w3.org/TR/mediaqueries-4/#descdef-media-any-pointer
更新(由于评论):上述解决方案是检测“粗指针”(通常是触摸屏)是否是主要输入设备。如果您想确定某个设备(如鼠标)是否也具有触摸屏,则可以any-pointer: coarse
改用它。
有关更多信息,请参见此处:检测浏览器没有鼠标并且仅是触摸式
(pointer: coarse)
因为您极有可能只针对主要输入。由于少数不支持的浏览器仅是台式机,因此可以在生产中使用。在css-tricks上有一篇很棒的文章。
我喜欢这一个:
function isTouchDevice(){
return typeof window.ontouchstart !== 'undefined';
}
alert(isTouchDevice());
function isTouchDevice(){ return (window.ontouchstart !== undefined); }
var isTouch = 'ontouchstart' in window;
,但是不适用于最新的Chrome(v31),var isTouch = 'createTouch' in window.document;
仍可以使用。
touchstart
将无法将Surface识别为触摸设备,因为IE改用pointer
事件。
如果您使用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
}
如果您不使用Modernizr,则只需将Modernizr.touch
上面的函数替换为('ontouchstart' in document.documentElement)
另请注意,与相比,测试用户代理iemobile
将为您提供更广泛的检测到的Microsoft移动设备Windows Phone
。
/(iphone|ipod|ipad|android|iemobile|blackberry|bada)/.test(window.navigator.userAgent.toLowerCase())
我们尝试了modernizr实现,但是检测触摸事件不再是一致的(IE 10在Windows桌面上具有触摸事件,IE 11可以正常工作,因为已经删除了触摸事件并添加了指针api)。
因此,只要我们不知道用户使用哪种输入类型,我们便决定将网站优化为触摸式网站。这比任何其他解决方案都更可靠。
我们的研究表明,大多数桌面用户在单击之前会将鼠标移到屏幕上,因此我们可以检测到他们并更改其行为,然后他们才能单击或悬停任何内容。
这是我们代码的简化版本:
var isTouch = true;
window.addEventListener('mousemove', function mouseMoveDetector() {
isTouch = false;
window.removeEventListener('mousemove', mouseMoveDetector);
});
我已经做到了这一点;
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
}
除了检查他们是否有触摸屏之外,还有其他要比检查他们是否正在使用触摸屏更好的方法,而且检查起来更容易。
if (window.addEventListener) {
var once = false;
window.addEventListener('touchstart', function(){
if (!once) {
once = true;
// Do what you need for touch-screens only
}
});
}
即使在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");
}
}
我使用上面的代码片段来检测是否触摸,因此我的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>
/iPhone|iPod|iPad|Android|BlackBerry/
?
试图检测触摸的最大“陷阱”是在同时支持触摸和触控板/鼠标的混合设备上。即使您能够正确检测用户设备是否支持触摸,您真正需要做的就是检测用户当前正在使用的输入设备。有关此挑战的详细记录,可能的解决方案。
基本上,确定用户是刚刚触摸屏幕还是使用鼠标/触控板的方法是在页面上注册a touchstart
和mouseover
event:
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
向文档根目录添加或删除类,以反映此时用户在文档上的当前输入类型:
;(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
})();
更多细节在这里。
查看这篇文章,它提供了一个非常不错的代码片段,用于检测到触摸设备时执行的操作或调用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();
}
不,不可能。给出的出色答案永远都是局部的,因为任何给定的方法都会产生假阳性和假阴性。由于OS API,即使浏览器也并不总是知道是否存在触摸屏,并且事实可能会在浏览器会话期间发生变化,尤其是在使用KVM类型的布置时。
请参阅这篇出色的文章中的更多细节:
http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
本文建议您重新考虑使您想要检测触摸屏的假设,它们可能是错误的。(我检查了自己的应用程序,但我的假设确实是错误的!)
本文的结论是:
对于布局,假设每个人都有触摸屏。鼠标用户可以使用大型UI控件,而触摸用户可以使用小型UI控件。悬停状态也是如此。
对于事件和交互,假设任何人都可以拥有触摸屏。相互实现键盘,鼠标和触摸交互,确保彼此之间不会相互阻塞。
这些工作很多,但要么需要jQuery,要么需要javascript linter抱怨语法。考虑到您的第一个问题需要一种“ JavaScript”(不是jQuery,不是Modernizr)的解决方案,这是一个每次都能使用的简单函数。它也尽可能地少。
function isTouchDevice() {
return !!window.ontouchstart;
}
console.log(isTouchDevice());
我要提到的最后一个好处是该代码与框架和设备无关。请享用!
function isTouchScreen() { return window.matchMedia('(hover: none)').matches;}
这对我有用,所以我在这里为这个答案做一个贡献,因为当Google将我带到这里时,我也期待一个香草JS线程。
Chrome 24现在似乎支持触摸事件,可能适用于Windows8。因此此处发布的代码不再起作用。现在,我要绑定触摸和单击事件,并确保仅调用一个事件,而不是尝试检测浏览器是否支持触摸:
myCustomBind = function(controlName, callback) {
$(controlName).bind('touchend click', function(e) {
e.stopPropagation();
e.preventDefault();
callback.call();
});
};
然后调用它:
myCustomBind('#mnuRealtime', function () { ... });
希望这可以帮助 !
jQuery v1.11.3
提供的答案中有很多很好的信息。但是,最近,我花了很多时间尝试将所有内容实际结合到一个可行的解决方案中,以完成两件事:
除了这篇文章和使用Javascript检测触摸屏设备之外,我发现Patrick Lauke的这篇文章非常有帮助: https /
这是代码...
$(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”事件或与触摸相关的任何其他类似事件。在这种情况下,它是超链接元素“ a”。
现在,您无需处理JavaScript中的常规鼠标单击操作,因为您可以使用CSS通过超链接“ a”元素的选择器来处理这些事件,如下所示:
/* unvisited link */
a:link
{
}
/* visited link */
a:visited
{
}
/* mouse over link */
a:hover
{
}
/* selected link */
a:active
{
}
注意:还有其他选择器...
var isTouchScreen = 'createTouch' in document;
要么
var isTouchScreen = 'createTouch' in document || screen.width <= 699 ||
ua.match(/(iPhone|iPod|iPad)/) || ua.match(/BlackBerry/) ||
ua.match(/Android/);
我想这将是更彻底的检查。
ua
提及navigator.userAgent
。此外,如果有人以非全屏模式打开浏览器,则通过屏幕宽度进行检测也会产生错误的结果。
对于如何在Javascript中检测页面是否显示在触摸屏设备上,我也进行了很多尝试,其中包括不同的选择。IMO,截至目前,尚无实际选项可正确检测到该选项。浏览器要么报告台式机上的触摸事件(因为操作系统可能支持触摸),要么某些解决方案无法在所有移动设备上正常工作。
最后,我意识到我从一开始就采用了错误的方法:如果我的页面在触摸和非触摸设备上看起来都相似,那么我也许根本不必担心检测属性:我的情况是在触摸设备上的按钮上停用工具提示,因为它们会导致双击,而我希望单击即可激活该按钮。
我的解决方案是重构视图,以使按钮上不需要工具提示,最后,我不需要使用所有都有缺点的方法从Javascript中检测触摸设备。
您可以安装现代化器并使用简单的触摸事件。这非常有效,并且可以在我测试过的所有设备(包括Windows表面)上使用!
我创建了一个jsFiddle
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)公共站点(无登录)
对UI进行编码以使其与这两个选项一起使用。
2)登录站点
捕获是否在登录表单上发生了鼠标移动,并将其保存到隐藏的输入中。该值与登录凭据一起传递并添加到用户的session中,因此可以在会话期间使用。
jQuery仅添加到登录页面:
$('#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)
由于混合设备使用触摸和鼠标输入的组合,因此您需要能够动态更改状态/变量,该状态/变量控制如果用户是触摸用户,是否应该运行一段代码。
触摸设备也会触发 mousemove
在点击时。
touchstart
事件触发,然后将其设置为true。已在Safari iOS和Android版Chrome上测试。
注意:不能100%确定MS Surface等的指针事件。
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);
}
实际上,我研究了这个问题并考虑了所有情况。因为这也是我项目中的大问题。因此,我达到以下功能,它适用于所有设备上所有浏览器的所有版本:
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(''));
};
提示:绝对会isTouchDevice
返回boolean
值。
到目前为止,这对我来说似乎还不错:
//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)
}
当连接了鼠标时,可以假定用户以相当高的命中率(实际上可以说是100%)在页面准备好后用户至少将鼠标移动了很小的距离-无需单击任何鼠标。下面的机制可以检测到这一点。如果检测到,我认为这是缺少触摸支持的标志,或者如果支持,则在使用鼠标时意义不大。如果未检测到,则假定为触摸设备。
编辑此方法可能无法满足所有目的。它可用于控制基于已加载页面上的用户交互(例如图像查看器)激活的功能。下面的代码还会使mousemove事件绑定在没有鼠标的设备上,因为它现在很突出。其他方法可能更好。
大致来说,它是这样的(对不起jQuery,但在纯Javascript中类似):
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;
}));