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

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

这可能吗?

616850 次浏览

更新2021

要查看以前的答案,请查看历史记录。我决定从头开始,因为在保存历史的时候,它变得难以控制。

我最初的回答说,使用与Modernizr使用的相同的功能可能是一个好主意,但这是无效的,因为他们删除了&;touchevents"测试这个PR: https://github.com/Modernizr/Modernizr/pull/2432,因为它是一个令人困惑的主题。

话虽如此,这应该是一个相当好的方法来检测浏览器是否具有“触摸功能”:

function isTouchDevice() {
return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
}

但是对于比我更聪明的人写过的更高级的用例,我建议阅读这些文章:

虽然它只是在alpha, Jquery移动框架值得一试。它将在移动浏览器中规范化这些类型的事件。也许可以看看他们在做什么。我假设jquery-mobile.js与这个框架有所不同。

更新:请阅读blmstr的答案下面,然后将整个特征检测库拉到您的项目。检测实际的触摸支持更加复杂,Modernizr只涵盖了一个基本的用例。

Modernizr是一个伟大的,轻量级的方式来做任何网站上的各种特征检测。

它只是为每个特性在html元素中添加类。

然后你可以在CSS和JS中轻松地瞄准这些功能。例如:

html.touch div {
width: 480px;
}


html.no-touch div {
width: auto;
}

和Javascript (jQuery示例):

$('html.touch #popup').hide();
var isTouchScreen = 'createTouch' in document;

var isTouchScreen = 'createTouch' in document || screen.width <= 699 ||
ua.match(/(iPhone|iPod|iPad)/) || ua.match(/BlackBerry/) ||
ua.match(/Android/);

我想会进行更彻底的检查。

看看这个帖子,它给出了一个非常好的代码片段,告诉你当检测到触摸设备时该做什么,或者当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();
}

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

我最终使用:

<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>

我使用:

if(jQuery.support.touch){
alert('Touch enabled');
}

jQuery mobile 1.0.1

我喜欢这个:

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


alert(isTouchDevice());

由于Modernizr无法检测Windows Phone 8/WinRT上的IE10,一个简单的跨浏览器解决方案是:

var supportsTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints;

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

我会避免使用屏幕宽度来判断一个设备是否是触控设备。触摸屏比699px大得多,想想Windows 8。Navigatior。userAgent可以很好地覆盖误报。

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

你是想测试设备是否支持触摸事件,还是一个触摸设备。不幸的是,这不是一回事。

看起来Chrome 24现在支持触摸事件了,可能是Windows 8。所以这里发布的代码不再有效。而不是试图检测触摸是否支持浏览器,我现在绑定触摸和点击事件,并确保只有一个被调用:

myCustomBind = function(controlName, callback) {


$(controlName).bind('touchend click', function(e) {
e.stopPropagation();
e.preventDefault();


callback.call();
});
};

然后调用它:

myCustomBind('#mnuRealtime', function () { ... });

希望这能有所帮助!

使用上面所有的注释,我已经组装了下面的代码,是为我的需要工作:

var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0));

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

它目前在Windows Phone 7上无法运行,我还没有找到针对该浏览器的解决方案。

希望有人觉得这有用。

如果你使用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,你可以简单地用('ontouchstart' in document.documentElement)替换上面的Modernizr.touch函数

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

也可以看到这个SO问题

这种方法对我来说很有效,它等待第一次用户交互,以确保他们是在触摸设备上

var touchEnabled = false;
$(document.body).one('touchstart',
function(e){
touchEnabled=true;
$(document.documentElement).addClass("touch");
// other touch related init
//
}
);

这个即使在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");
}
}

jQuery support对象:

jQuery.support.touch = 'ontouchend' in document;

现在你可以在任何地方检查它,像这样:

if( jQuery.support.touch )
// do touch stuff

所有浏览器支持,除了Firefox桌面总是真正的,因为Firefox桌面支持响应式设计的开发人员,即使你点击触摸按钮或不!

我希望Mozilla在下一个版本中解决这个问题。

我用的是火狐28桌面版。

function isTouch()
{
return !!("ontouchstart" in window) || !!(navigator.msMaxTouchPoints);
}
我还在如何用Javascript检测页面是否显示在触摸屏设备上的不同选项上挣扎了很多。 在我看来,到目前为止,还没有真正的选项可以正确地检测这个选项。 浏览器要么报告桌面机器上的触摸事件(因为操作系统可能已经准备好触摸了),要么某些解决方案无法在所有移动设备上工作 最后,我意识到我从一开始就采取了错误的方法: 如果我的页面在触摸和非触摸设备上看起来相似,我可能根本不需要担心检测属性: 我的场景是禁用触控设备上的按钮上的工具提示,因为它们会导致双击,而我想要的是一次点击来激活按钮

我的解决方案是重构视图,这样就不需要按钮上的工具提示,最后我不需要用都有缺点方法从Javascript检测触摸设备。

你可以安装modernizer并使用一个简单的触摸事件。这是非常有效的,工作在每一个设备上,我已经测试了它包括窗户表面!

我已经创建了一个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;
}
}

我们尝试了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
}

许多这些工作,但要么需要jQuery,或javascript linters抱怨语法。考虑到你最初的问题要求“JavaScript”(不是jQuery,不是Modernizr)来解决这个问题,这里有一个简单的函数,每次都能工作。这也是你能得到的最小值。

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


console.log(isTouchDevice());

我要提到的最后一个好处是,该代码是框架和设备不可知的。享受吧!

不,不可能。给出的优秀答案永远都是片面的,因为任何给定的方法都会产生假阳性和假阴性。由于操作系统api的原因,甚至浏览器也不总是知道是否存在触摸屏,而且在浏览器会话期间,这一事实可能会发生变化,特别是使用kvm类型的安排。

详见这篇优秀的文章:

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

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

文章总结道:

对于布局,假设每个人都有触摸屏。鼠标用户可以使用 大型UI控件比触摸用户使用小控件容易得多 的人。

对于事件和交互,假设任何人都有触摸屏。 实现键盘、鼠标和触摸交互,

.

.

有一种方法比检查他们是否拥有触摸屏更好,那就是检查他们是否正在使用触摸屏,而且这更容易检查。

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

实际的答案似乎是一个考虑上下文的答案:

1)公共场所(没有登录)
编写UI代码以同时使用这两个选项 < p > 2)登录站点 < br > 捕获登录表单上是否发生了鼠标移动,并将其保存到隐藏的输入中。该值与登录凭据一起传递,并添加到用户的会话中,因此它可以在会话期间使用

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);
});
}

(@Dave Burt +1, @Martin Lantzsch +1)

jQuery v1.11.3

答案中有很多有用的信息。但是,最近我花了很多时间试图将所有事情结合到一个有效的解决方案中,以完成两件事:

  1. 检测正在使用的设备是触摸屏类型的设备。
  2. 检测设备被窃听。

除了这篇文章和用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
{


}

注意:还有其他的选择器…

到目前为止,这似乎对我来说很有效:

//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%),用户在页面准备好后移动鼠标至少一小段距离-没有任何点击。下面的机制检测到这一点。如果检测到,我认为这是缺少触摸支持的标志,或者,如果支持,在使用鼠标时不太重要。如果未检测到触摸设备,则假定为触摸设备。

这种方法可能不适用于所有目的。它可以用来控制基于加载页面上的用户交互激活的功能,例如图像查看器。下面的代码还将把mouemove事件绑定在没有鼠标的设备上,因为它现在很突出。其他方法可能更好。

大致是这样的(对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;
}));

试图检测触控的最大“陷阱”是同时支持触控和触控板/鼠标的混合设备。即使你能够正确地检测用户的设备是否支持触摸,你真正需要做的是检测用户目前使用的是什么输入设备。这里有一个关于这个挑战和这里有一个可能的解决方案的详细描述。

基本上,判断用户是触摸屏幕还是使用鼠标/触控板的方法是在页面上注册touchstartmouseover事件:

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

更多细节在这里

这个问题

由于混合设备使用触摸和鼠标输入的组合,你需要能够动态改变状态/变量,它控制一段代码是否应该运行,如果用户是触摸用户或不是。

触摸设备也在水龙头上发射mousemove

解决方案

  1. 假设触摸在负载上为假。
  2. 等待touchstart事件被触发,然后将其设置为true。
  3. 如果touchstart被触发,添加一个鼠标移动处理程序。
  4. 如果两个mouemove事件触发之间的时间小于20ms,则假设它们使用鼠标作为输入。删除事件,因为它不再需要,鼠标移动是一个昂贵的事件鼠标设备。
  5. 一旦touchstart再次被触发(用户回到使用touch),变量被设置回true。重复这个过程,以动态的方式确定。如果鼠标移动奇迹般地在触摸时快速触发两次(在我的测试中,这几乎不可能在20毫秒内完成),下一次touchstart将会将其设置为true。

在Safari iOS和Chrome for Android上进行了测试。

注意:不是100%确定的指针事件为MS Surface等。

< a href = " https://codepen。io/anon/pen/wpvLqo" rel="nofollow noreferrer"> code depen demo . wpvLqo" rel="nofollow noreferrer">


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);
}

您可以使用以下代码:

function isTouchDevice() {
var el = document.createElement('div');
el.setAttribute('ongesturestart', 'return;'); // or try "ontouchstart"
return typeof el.ongesturestart === "function";
}

来源:检测基于触摸的浏览@mplungjan帖子

上面的解决方案是基于检测事件支持,而不需要浏览器嗅探文章。

你可以在下面的测试页面处检查结果。

请注意,上面的代码只测试浏览器是否支持触摸,而不是设备本身。所以如果你的笔记本电脑有触摸屏,你的浏览器可能不支持触摸事件。最近的Chrome浏览器支持触摸事件,但其他浏览器可能没有。

你也可以试试:

if (document.documentElement.ontouchmove) {
// ...
}

但它可能不适用于iPhone设备。

$.support.touch ? "true" : "false";

如果你测试document.documentElement中支持touchstart,这就很简单了

var x = 'touchstart' in document.documentElement;
console.log(x)
// return true if is supported
// else return false

自从交互媒体功能的引入,你可以简单地做:

if(window.matchMedia("(pointer: coarse)").matches) {
// touchscreen
}

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

更新(由于评论):上面的解决方案是检测“粗指针”——通常是触摸屏——是否是主要输入设备。如果你想检测一个带有鼠标的设备是否也有触摸屏,你可以使用any-pointer: coarse代替。

要了解更多信息,请查看这里:检测到浏览器没有鼠标,只能触摸

我认为最好的方法是:

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

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

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(''));
};

< em >提示< / em >:当然,isTouchDevice只返回boolean值。

所以关于检测触摸/非触摸设备存在很大的争议。窗口平板电脑的数量和尺寸都在增加,这给我们网页开发者带来了另一个难题。

我已经使用和测试blmstr的回答菜单。菜单的工作方式是这样的:当页面加载时,脚本检测这是一个触摸或非触摸设备。基于此,菜单将在悬停(非触摸)或点击/点击(触摸)时工作。

在大多数情况下,blmstr的脚本似乎工作得很好(特别是2018年的那个)。但仍然有一种设备,当它不是触摸时,它会被检测为触摸,反之亦然。

出于这个原因,我做了一些挖掘和多亏了这篇文章,我从blmstr的第4个脚本替换了几行:

function is_touch_device4() {
if ("ontouchstart" in window)
return true;


if (window.DocumentTouch && document instanceof DocumentTouch)
return true;




return window.matchMedia( "(pointer: coarse)" ).matches;
}


alert('Is touch device: '+is_touch_device4());
console.log('Is touch device: '+is_touch_device4());

由于封锁,有有限的触摸设备来测试这一个,但到目前为止,上面的工作很好。

如果任何人有桌面触摸设备(如平板电脑)可以确认脚本是否工作正常,我将不胜感激。

现在在支持指针方面:粗媒体查询似乎是支持的。我保留了上面的行,因为我在移动firefox上(出于某种原因)出现了问题,但媒体查询上面的行是有效的。

谢谢