我编写了一个 jQuery 插件,可以在桌面和移动设备上使用。我想知道 JavaScript 是否有办法检测设备是否具有触摸屏功能。我正在使用 jquery-mobile.js 来检测触摸屏事件,它适用于 iOS、Android 等,但我还想根据用户的设备是否有触摸屏来编写条件语句。
那可能吗?
2021 年更新
要查看旧答案:查看历史记录。我决定从头开始,因为在将历史记录在帖子中时它会失控。
我最初的回答说,使用与 Modernizr 使用的相同功能可能是个好主意,但这不再有效,因为他们删除了此 PR:https://github.com/Modernizr/Modernizr/pull/2432 上的“touchevents”测试,因为它是一个令人困惑的主题。
话虽如此,这应该是检测浏览器是否具有“触摸功能”的一种相当不错的方法:
function isTouchDevice() {
return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
}
但是对于更高级的用例,比我写的关于这个主题的人更聪明,我建议阅读这些文章:
Stu Cox:你无法检测到触摸屏
检测触摸:这是“为什么”,而不是“如何”
Patrick H. Lauke 的精彩演讲
更新:在将整个特征检测库引入您的项目之前,请阅读下面的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 在 Windows Phone 8/WinRT 上没有检测到 IE10,一个简单的跨浏览器解决方案是:
var supportsTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints;
您只需要检查一次,因为设备不会突然支持或不支持触摸,因此只需将其存储在一个变量中,这样您就可以更有效地多次使用它。
由于引入了交互媒体功能,您只需执行以下操作:
if(window.matchMedia("(pointer: coarse)").matches) {
// touchscreen
}
https://www.w3.org/TR/mediaqueries-4/#descdef-media-any-pointer
更新(由于评论):上述解决方案是检测“粗略指针”(通常是触摸屏)是否是主要输入设备。如果您想检测带有鼠标的设备是否也有触摸屏,您可以改用 any-pointer: coarse
。
有关详细信息,请查看此处:Detecting that the browser has no mouse and is touch-only
(pointer: coarse)
,因为您很可能只针对主要输入。可以在生产中使用,因为少数不支持的浏览器只是桌面。关于这个 on css-tricks 有一篇很棒的文章。
使用上面的所有评论,我已经组装了以下满足我需要的代码:
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
:(
我喜欢这个:
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;
仍然有效。
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
将为您提供比 Windows Phone
更广泛的检测到的 Microsoft 移动设备。
我们尝试了modernizr 实现,但检测触摸事件不再一致(IE 10 在 Windows 桌面上有触摸事件,IE 11 工作,因为已经删除了触摸事件并添加了指针 api)。
所以我们决定将网站优化为触摸网站,只要我们不知道用户的输入类型。这比任何其他解决方案都更可靠。
我们的研究表明,大多数桌面用户在点击之前会在屏幕上移动鼠标,因此我们可以检测到他们并在他们能够点击或悬停任何东西之前改变他们的行为。
这是我们代码的简化版本:
var isTouch = true;
window.addEventListener('mousemove', function mouseMoveDetector() {
isTouch = false;
window.removeEventListener('mousemove', mouseMoveDetector);
});
有比检查他们是否有触摸屏更好的方法是检查他们是否正在使用它,而且检查起来更容易。
if (window.addEventListener) {
var once = false;
window.addEventListener('touchstart', function(){
if (!once) {
once = true;
// Do what you need for touch-screens only
}
});
}
我是这样实现的;
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 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");
}
}
尝试检测触摸的最大“陷阱”是在支持触摸和触控板/鼠标的混合设备上。即使您能够正确检测到用户的设备是否支持触摸,您真正需要做的是检测用户当前使用的输入设备。对此挑战和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。
我使用上面的代码片段来检测是否触摸,所以我的 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/
的单个匹配调用?
实际上,我研究了这个问题并考虑了所有情况。因为这也是我项目的一个大问题。所以我达到了以下功能,它适用于所有设备上所有浏览器的所有版本:
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
值。
查看此 post,它提供了一个非常好的代码片段,用于说明检测到触摸设备时的操作或调用 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();
}
我会避免使用屏幕宽度来确定设备是否是触摸设备。有比 699 像素大得多的触摸屏,想想 Windows 8。 Navigatior.userAgent 可能会很好地覆盖错误消息。
我建议在 Modernizr 上查看 this issue。
您是否要测试设备是否支持触摸事件或者是触摸设备。不幸的是,这不是一回事。
不,这是不可能的。给出的优秀答案只是部分的,因为任何给定的方法都会产生假阳性和假阴性。由于操作系统 API,即使浏览器也不总是知道是否存在触摸屏,而且在浏览器会话期间这一事实可能会发生变化,尤其是在 KVM 类型的安排下。
在这篇优秀的文章中查看更多细节:
http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
这篇文章建议你重新考虑那些让你想要检测触摸屏的假设,它们可能是错误的。 (我检查了我自己的应用程序,我的假设确实是错误的!)
文章总结:
对于布局,假设每个人都有触摸屏。鼠标用户使用大型 UI 控件比触摸用户使用小型控件更容易。悬停状态也是如此。对于事件和交互,假设任何人都可能有触摸屏。实现键盘、鼠标和触摸交互并排,确保互不阻碍。
其中许多工作,但要么需要 jQuery,要么 javascript linter 抱怨语法。考虑到您最初的问题要求使用“JavaScript”(不是 jQuery,不是 Modernizr)方法来解决这个问题,这里有一个每次都可以使用的简单函数。它也是尽可能少的。
function isTouchDevice() {
return !!window.ontouchstart;
}
console.log(isTouchDevice());
我要提到的最后一个好处是此代码与框架和设备无关。享受!
看起来 Chrome 24 现在支持触摸事件,可能适用于 Windows 8。因此此处发布的代码不再有效。我现在不是尝试检测浏览器是否支持触摸,而是绑定触摸和点击事件,并确保只调用一个:
myCustomBind = function(controlName, callback) {
$(controlName).bind('touchend click', function(e) {
e.stopPropagation();
e.preventDefault();
callback.call();
});
};
然后调用它:
myCustomBind('#mnuRealtime', function () { ... });
希望这可以帮助 !
支持除桌面版 Firefox 之外的所有浏览器始终为 TRUE,因为桌面版 Firefox 支持开发人员的响应式设计,即使您点击或不点击触摸按钮!
我希望 Mozilla 会在下一个版本中解决这个问题。
我正在使用 Firefox 28 桌面版。
function isTouch()
{
return !!("ontouchstart" in window) || !!(navigator.msMaxTouchPoints);
}
true
:(
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
{
}
注意:还有其他选择器...
问题
由于混合设备使用触摸和鼠标输入的组合,您需要能够动态更改状态/变量,该状态/变量控制如果用户是触摸用户,是否应该运行一段代码。
触摸设备也会在点击时触发 mousemove
。
解决方案
假设在加载时触摸是错误的。等到触发 touchstart 事件,然后将其设置为 true。如果触发了 touchstart,则添加一个 mousemove 处理程序。如果两个 mousemove 事件触发之间的时间小于 20 毫秒,则假设它们使用鼠标作为输入。删除不再需要的事件,并且 mousemove 对鼠标设备来说是一个昂贵的事件。一旦再次触发 touchstart(用户重新使用触摸),该变量就会设置回 true。并重复该过程,以便以动态方式确定它。如果由于某种奇迹,mousemove 在触摸时被触发了两次(在我的测试中几乎不可能在 20 毫秒内完成),下一次 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);
}
因此,关于检测触摸/非触摸设备存在巨大争议。窗口平板电脑的数量和平板电脑的尺寸正在增加,这给我们的 Web 开发人员带来了另一组头痛。
我已经为菜单使用并测试了 blmstr's answer。菜单的工作方式如下:当页面加载时,脚本会检测这是触摸设备还是非触摸设备。基于此,菜单可以在悬停(非触摸)或点击/点击(触摸)时工作。
在大多数情况下,blmstr 的脚本似乎工作得很好(特别是 2018 年的脚本)。但是,仍然有一个设备在没有触摸时会被检测为触摸,反之亦然。
出于这个原因,我进行了一些挖掘,thanks to this article 我将 blmstr 的第 4 个脚本中的几行替换为:
function is_touch_device4() { if ("ontouchstart" in window) return true; if (window.DocumentTouch && document instanceof DocumentTouch) 返回真; return window.matchMedia("(指针:粗略)").matches; } alert('是触摸设备:'+is_touch_device4()); console.log('是触摸设备:'+is_touch_device4());
由于锁定,用于测试此设备的触摸设备供应有限,但到目前为止,上述方法效果很好。
如果有桌面触摸设备(例如 Surface 平板电脑)的人可以确认脚本是否正常工作,我将不胜感激。
现在在支持指针方面:似乎支持粗略的媒体查询。我保留了上面的行,因为我(出于某种原因)在移动 Firefox 上遇到问题,但媒体查询上方的行可以解决问题。
谢谢
我认为最好的方法是:
var isTouchDevice =
(('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
if(!isTouchDevice){
/* Code for touch device /*
}else{
/* Code for non touch device */
}
navigator.MaxTouchPoints
-> navigator.maxTouchPoints
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
。此外,如果有人以非全屏模式打开浏览器,则按屏幕宽度进行的检测可能会给出错误的结果。
我用:
if(jQuery.support.touch){
alert('Touch enabled');
}
在 jQuery 移动版 1.0.1 中
您可以安装 Modernizer 并使用简单的触摸事件。这非常有效,适用于我测试过的所有设备,包括 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;
}
}
关于如何在 Javascript 中检测页面是否显示在触摸屏设备上的不同选项,我也遇到了很多困难。 IMO,截至目前,不存在正确检测该选项的真正选项。浏览器要么报告桌面机器上的触摸事件(因为操作系统可能支持触摸),要么某些解决方案不适用于所有移动设备。
最后,我意识到我从一开始就采用了错误的方法:如果我的页面在触摸和非触摸设备上看起来相似,我可能根本不必担心检测属性:我的场景是停用触摸设备上按钮上的工具提示,因为它们会导致双击,我希望单击以激活按钮。
我的解决方案是重构视图,这样按钮上就不需要工具提示,最后我不需要从 Javascript 中检测触摸设备,这些方法都有其缺点。
实际的答案似乎是考虑上下文的答案:
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 的答案)
范围 jQuery support
对象:
jQuery.support.touch = 'ontouchend' in document;
现在您可以在任何地方检查它,如下所示:
if( jQuery.support.touch )
// do touch stuff
true
或false
。您可以在此处阅读更多信息:stackoverflow.com/questions/4686583/…in
运算符已经计算为布尔值。'onmsgesturechange'
即使在非触摸设备 (PC) 中也评估为真。window.navigator.msMaxTouchPoints
似乎更准确。 Found it here。