使用 JavaScript 检测“触摸屏”设备的最佳方法是啥?
Posted
技术标签:
【中文标题】使用 JavaScript 检测“触摸屏”设备的最佳方法是啥?【英文标题】:What's the best way to detect a 'touch screen' device using JavaScript?使用 JavaScript 检测“触摸屏”设备的最佳方法是什么? 【发布时间】:2011-06-16 13:34:16 【问题描述】:我编写了一个 jQuery 插件,可以在桌面和移动设备上使用。我想知道 javascript 是否有办法检测设备是否具有触摸屏功能。我正在使用 jquery-mobile.js 来检测触摸屏事件,它适用于 ios、android 等,但我还想根据用户的设备是否有触摸屏来编写条件语句。
这可能吗?
【问题讨论】:
这是更好的方法 var x = 'touchstart' in document.documentElement; console.log(x) // 如果支持则返回 true // 否则返回 false 如果新技术还在涌现,为什么这个帖子会受到保护? 【参考方案1】:2021 年更新
要查看旧答案:查看历史记录。我决定从头开始,因为在帖子中保留历史记录时它会失控。
我最初的回答说,使用与 Modernizr 相同的功能可能是个好主意,但这不再有效,因为他们删除了此 PR 上的“touchevents”测试:https://github.com/Modernizr/Modernizr/pull/2432,因为它是令人困惑的主题。
话虽如此,这应该是检测浏览器是否具有“触摸功能”的一种相当不错的方法:
function isTouchDevice()
return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
但是对于更高级的用例,比我写的关于这个主题的人要聪明得多,我建议阅读这些文章:
Stu Cox: You Can't Detect a Touchscreen Detecting touch: it's the 'why', not the 'how' Getting touchy presentation by Patrick H. Lauke【讨论】:
双键将值转换为布尔值,强制函数返回true
或false
。你可以在这里阅读更多信息:***.com/questions/4686583/…
这不适用于 Opera Mobile 10 或 Internet Explorer Mobile 6 (Windows Mobile 6.5)。
双非 (!!) 是多余的,因为 in
运算符已经计算为布尔值。
'onmsgesturechange'
即使在非触摸设备 (PC) 中也被评估为 true。 window.navigator.msMaxTouchPoints
似乎更准确。 Found it here.
尽管我的屏幕没有触摸传感器,但它在 Windows 8 上的 IE10 上评估为 true。我将旧笔记本电脑从 Windows 7 升级到了 Windows 8。【参考方案2】:
虽然它只是 Alpha 版,但 jquery mobile framework 值得一试。它将跨移动浏览器规范这些类型的事件。也许看看他们在做什么。我假设 jquery-mobile.js 与这个框架不同。
【讨论】:
jquery mobile 很棒,但我们将它用于移动开发,但我希望能够专门针对触摸屏设备进行测试。仅使用 jquery mobile 似乎没有办法做到这一点。 对,这样不行。它使用渐进增强。【参考方案3】:更新:在将整个特征检测库拉入您的项目之前,请阅读下面的blmstr's answer。检测实际的触摸支持更为复杂,Modernizr 仅涵盖一个基本用例。
Modernizr 是一种出色的轻量级方法,可以在任何网站上进行各种特征检测。
它只是为每个功能的 html 元素添加类。
然后,您可以在 CSS 和 JS 中轻松定位这些功能。例如:
html.touch div
width: 480px;
html.no-touch div
width: auto;
和 Javascript(jQuery 示例):
$('html.touch #popup').hide();
【讨论】:
Modernizr 不测试触摸屏。它测试浏览器中是否存在触摸事件。请参阅文档中的“其他测试”部分:modernizr.com/docs/#features-misc【参考方案4】: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
。此外,如果有人以非全屏模式打开浏览器,则按屏幕宽度进行的检测可能会给出错误的结果。【参考方案5】:
查看这个post,它提供了一个非常棒的代码 sn-p,用于说明检测到触摸设备时的操作或调用 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();
【讨论】:
【参考方案6】:我使用上面的代码片段来检测是否触摸,所以我的 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/
的单个匹配调用?
Opera Mini 在 Opera 的服务器上而不是在设备本身上进行渲染,所以这种方式有点奇怪。【参考方案7】:
我用:
if(jQuery.support.touch)
alert('Touch enabled');
在 jQuery 移动版 1.0.1 中
【讨论】:
【参考方案8】:我喜欢这个:
function isTouchDevice()
return window.ontouchstart !== undefined;
alert(isTouchDevice());
【讨论】:
不需要使用三元表达式来返回布尔值。只需使用表达式返回布尔值。function isTouchDevice() return (window.ontouchstart !== undefined);
您也可以只使用:var isTouch = 'ontouchstart' in window;
,但这不适用于最新的 Chrome(v31),var isTouch = 'createTouch' in window.document;
仍然有效。
正如之前接受的问题的 cmets 中已经提到的那样。 “Modernizr 不测试触摸屏。它测试浏览器中是否存在触摸事件”。您的功能在技术上是 hasTouchEvents() 而不是 isTouchDevice()
请注意,仅测试 touchstart
的类似方法将无法将 Surface 识别为触控设备,因为 IE 使用的是 pointer
事件。
也许@Nis 是对的,但在 Firefox 39 中,它正确返回 false。【参考方案9】:
由于 Modernizr 无法在 Windows Phone 8/WinRT 上检测到 IE10,因此一个简单的跨浏览器解决方案是:
var supportsTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints;
您只需要检查一次,因为设备不会突然支持或不支持触摸,因此只需将其存储在一个变量中,这样您就可以更有效地多次使用它。
【讨论】:
【参考方案10】:我会避免使用屏幕宽度来确定设备是否为触摸设备。有比 699 像素大得多的触摸屏,想想 Windows 8。 Navigatior.userAgent 可能会很好地覆盖错误消息。
我建议在 Modernizr 上查看 this issue。
您是否要测试设备是否支持触摸事件或是否为触摸设备。不幸的是,这不是一回事。
【讨论】:
【参考方案11】:看起来 Chrome 24 现在支持触摸事件,可能适用于 Windows 8。因此此处发布的代码不再有效。我现在不是尝试检测浏览器是否支持触摸,而是绑定触摸和点击事件,并确保只调用一个:
myCustomBind = function(controlName, callback)
$(controlName).bind('touchend click', function(e)
e.stopPropagation();
e.preventDefault();
callback.call();
);
;
然后调用它:
myCustomBind('#mnuRealtime', function () ... );
希望这会有所帮助!
【讨论】:
相关话题,***.com/questions/12566306/…【参考方案12】:使用上面的所有 cmets,我已经组装了以下满足我需要的代码:
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 上失败,我还没有找到适合该浏览器的解决方案。
希望有人觉得这很有用。
【讨论】:
有什么不能使用的原因:function is_touch_device() return !!('ontouchstart' in window) || !!(导航器中的“msmaxtouchpoints”); ; 使用该函数会起作用,但我通常喜欢使用上面的变量方法,因此它只测试一次,并且稍后在我的代码中检查时会更快。我还发现我需要测试 msMaxTouchPoints 是否大于 0,因为没有触摸屏的 Windows 8 上的 IE 10 返回 0 作为 msMaxTouchPoints。 在 Windows 7 上的 Firefox 32 上返回true
:(
Windows 8 上的 Firefox 33 和 33.1 在我的系统上都正确显示为 false。如果您将 Firefox 升级到最新版本,它仍然返回 true 吗?您的机器上是否安装了可能使 Firefox 错误地认为您的机器具有触控功能的设备?
>>> 'ontouchstart' in window >>> true FF 51.0.1 on ubuntu【参考方案13】:
如果你使用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
将提供比 Windows Phone
更广泛的检测到的 Microsoft 移动设备。
Also see this SO question
【讨论】:
“做一些敏感的事情”和“不能碰这个”的奖励积分很多【参考方案14】:这种方式适合我,它等待第一次用户交互以确保他们在触摸设备上
var touchEnabled = false;
$(document.body).one('touchstart',
function(e)
touchEnabled=true;
$(document.documentElement).addClass("touch");
// other touch related init
//
);
【讨论】:
【参考方案15】:即使在 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");
【讨论】:
【参考方案16】:扩展 jQuery support
对象:
jQuery.support.touch = 'ontouchend' in document;
现在你可以在任何地方检查它,就像这样:
if( jQuery.support.touch )
// do touch stuff
【讨论】:
【参考方案17】:除了桌面版 Firefox 之外的所有浏览器都支持 TRUE,因为桌面版 Firefox 支持开发人员的响应式设计,即使您点击或不点击触摸按钮!
我希望 Mozilla 会在下一个版本中解决这个问题。
我使用的是 Firefox 28 桌面版。
function isTouch()
return !!("ontouchstart" in window) || !!(navigator.msMaxTouchPoints);
【讨论】:
它仍然是 32.0 版本,他们还没有修复它!疯狂的。为什么这不能是可切换的?这总是返回true
:(【参考方案18】:
对于如何在 Javascript 中检测页面是否显示在触摸屏设备上的不同选项,我也遇到了很多困难。 IMO,截至目前,不存在正确检测该选项的真正选项。 浏览器要么报告桌面机器上的触摸事件(因为操作系统可能支持触摸),要么某些解决方案不适用于所有移动设备。
最后,我意识到我从一开始就采用了错误的方法: 如果我的页面在触摸和非触摸设备上看起来相似,我可能根本不必担心检测属性: 我的方案是停用触摸设备上按钮上的工具提示,因为它们会导致双击,我希望单击以激活按钮。
我的解决方案是重构视图,这样按钮上就不需要工具提示,最后我不需要使用 all 的方法从 Javascript 中检测触摸设备有它们的缺点。
【讨论】:
【参考方案19】:您可以安装 Modernizer 并使用简单的触摸事件。这非常有效,适用于我测试过的所有设备,包括 Windows Surface!
我创建了一个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;
【讨论】:
欢迎来到 SO!代码本身(如未注释的代码)很少构成答案。您可以通过添加对 sn-p 的解释来改进此答案。【参考方案20】:我们尝试了 modernizr 实现,但检测到触摸事件不再一致(IE 10 在 Windows 桌面上有触摸事件,IE 11 工作,因为已经删除了触摸事件并添加了指针 api)。
所以我们决定将网站优化为触摸网站,只要我们不知道用户的输入类型。这比任何其他解决方案都更可靠。
我们的研究表明,大多数桌面用户在点击之前都会将鼠标悬停在屏幕上,因此我们可以检测到他们并在他们能够点击或悬停任何东西之前改变他们的行为。
这是我们代码的简化版本:
var isTouch = true;
window.addEventListener('mousemove', function mouseMoveDetector()
isTouch = false;
window.removeEventListener('mousemove', mouseMoveDetector);
);
【讨论】:
【参考方案21】:Working Fiddle
我是这样实现的;
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
【讨论】:
【参考方案22】:其中许多工作,但要么需要 jQuery,要么 javascript linter 抱怨语法。考虑到您最初的问题要求使用“JavaScript”(不是 jQuery,不是 Modernizr)方法来解决这个问题,这里有一个每次都可以使用的简单函数。它也是尽可能少的。
function isTouchDevice()
return !!window.ontouchstart;
console.log(isTouchDevice());
我要提到的最后一个好处是此代码与框架和设备无关。享受吧!
【讨论】:
【参考方案23】:不,这是不可能的。给出的优秀答案只是部分的,因为任何给定的方法都会产生假阳性和假阴性。由于操作系统 API,即使浏览器也不总是知道是否存在触摸屏,而且在浏览器会话期间这一事实可能会发生变化,尤其是在 KVM 类型的安排下。
在这篇优秀的文章中查看更多详细信息:
http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
这篇文章建议您重新考虑那些让您想要检测触摸屏的假设,它们可能是错误的。 (我检查了我自己的应用程序,我的假设确实是错误的!)
文章总结:
对于布局,假设每个人都有触摸屏。鼠标用户可以使用 大型 UI 控件比触摸用户更容易使用小型 那些。悬停状态也是如此。
对于事件和交互,假设任何人都可能拥有触摸屏。 实现键盘、鼠标和触摸交互, 确保没有互相阻挡。
【讨论】:
【参考方案24】:有比检查他们是否有触摸屏更好的方法是检查他们是否正在使用它,而且这更容易检查。
if (window.addEventListener)
var once = false;
window.addEventListener('touchstart', function()
if (!once)
once = true;
// Do what you need for touch-screens only
);
【讨论】:
【参考方案25】:实际的答案似乎是一个考虑上下文的答案:
1) 公共站点(无需登录) 对 UI 进行编码以同时使用这两个选项。
2) 登录网站 捕获登录表单上是否发生鼠标移动,并将其保存到隐藏的输入中。该值与登录凭据一起传递并添加到用户的会话,因此可以在会话期间使用。
仅添加到登录页面的 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 的答案)
【讨论】:
【参考方案26】:jQuery v1.11.3
提供的答案中有很多很好的信息。但是,最近我花了很多时间尝试将所有内容整合到一个可行的解决方案中,以完成两件事:
-
检测正在使用的设备是触摸屏类型的设备。
检测到设备被窃听。
除了这篇文章和 Detecting touch screen devices with Javascript,我发现 Patrick Lauke 的这篇文章非常有帮助:https://hacks.mozilla.org/2013/04/detecting-touch-its-the-why-not-the-how/
这里是代码...
$(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
注意:还有其他选择器...
【讨论】:
【参考方案27】:到目前为止,这对我来说似乎工作正常:
//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)
【讨论】:
【参考方案28】:当连接鼠标时,可以假设用户在页面准备好后将鼠标移动至少一小段距离(没有任何点击)具有相当高的命中率(我会说几乎是 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;
));
【讨论】:
【参考方案29】:尝试检测触摸的最大“陷阱”是在同时支持触摸和触控板/鼠标的混合设备上。即使您能够正确检测用户的设备是否支持触摸,您真正需要做的是检测用户当前使用的输入设备。这个挑战和a possible solution here 有详细的记录。
确定用户是刚刚触摸屏幕还是使用鼠标/触控板的方法基本上是在页面上同时注册touchstart
和mouseover
事件:
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
)();
更多详情here。
【讨论】:
【参考方案30】:问题
由于混合设备使用触摸和鼠标输入的组合,您需要能够动态地更改状态/变量,该状态/变量控制在用户触摸时是否应该运行一段代码用户与否。
触摸设备也会在点击时触发mousemove
。
解决方案
-
假设加载时触摸为假。
等到
touchstart
事件被触发,然后将其设置为true。
如果触发了 touchstart,请添加 mousemove 处理程序。
如果两个 mousemove 事件触发之间的时间小于 20 毫秒,则假定它们使用鼠标作为输入。删除不再需要的事件,并且 mousemove 对鼠标设备来说是一个昂贵的事件。
一旦再次触发 touchstart(用户重新使用触摸),该变量就会重新设置为 true。并重复该过程,以便以动态方式确定它。如果由于某种奇迹,mousemove 在触摸时被快速触发两次(在我的测试中几乎不可能在 20 毫秒内完成),那么下一次 touchstart 会将其设置为 true。
在 Safari iOS 和 Android 版 Chrome 上测试。
注意:不能 100% 确定 MS Surface 等的指针事件。
Codepen demo
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);
【讨论】:
以上是关于使用 JavaScript 检测“触摸屏”设备的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
用于桌面触摸屏应用程序的 JavaScript 框架? [关闭]
在带有触摸屏的 Windows 8 上检测 Chrome 中的触摸事件