检测浏览器没有鼠标且只支持触摸屏的情况。
检测浏览器没有鼠标且只支持触摸屏的情况。
我正在开发一个具有非常不同的触摸界面(点击时,您的手指会隐藏屏幕)和鼠标界面(依赖于悬停预览)的Web应用程序(不是包含有趣文本页面的网站)。\n我如何检测到我的用户没有使用鼠标,以便为他们提供正确的界面?我计划为那些同时具有鼠标和触摸功能的人留下一个开关(例如一些笔记本电脑)。\n浏览器中的触摸事件能力并不意味着用户正在使用触摸设备(例如,Modernizr并不能解决这个问题)。正确回答这个问题的代码应该在设备有鼠标时返回false,在没有鼠标时返回true。对于同时具有鼠标和触摸功能的设备,它应该返回false(不是仅触摸)。\n作为一个附注,我的触摸界面也可能适用于只有键盘的设备,所以我更希望检测到缺少鼠标。\n为了更清楚地说明需求,这是我想要实现的API:\n
// Level 1 // 目前的答案提供了一种实现方法。 hasTouch(); // 如果期望有鼠标,则返回true。 // 注意:正如问题描述者所解释的那样,这不是!hasTouch() // 我认为我们还没有在现有答案中提供这个方法,所以我提供了一个奖励 hasMouse(); // Level 2(我认为这是不可能的,但也许我错了,所以为什么不问问) // 当“hasTouch()”的结果发生变化时,调用回调函数。 listenHasTouchChanges(callback); // 当“hasMouse()”的结果发生变化时,调用回调函数。 listenHasMouseChanges(callback);
检测浏览器是否没有鼠标且只能通过触摸进行操作的方法有很多,以下是一种可能的解决方案:
首先,可以在文档上监听mousemove事件,并在此事件被触发之前,假设设备只能通过触摸或键盘进行操作。代码如下:
var mouseDetected = false; function onMouseMove(e) { unlisten('mousemove', onMouseMove, false); mouseDetected = true; // initializeMouseBehavior(); } listen('mousemove', onMouseMove, false);
其中,listen和unlisten是根据需要委托给addEventListener或attachEvent的函数。
然而,这种方法可能会导致页面出现视觉上的不连续,特别是如果页面布局需要根据设备模式进行大规模重新布局的话。
虽然这是一个好主意,但是响应的延迟会使得当应用程序的用户界面依赖于鼠标是否可用时,这种方法变得不可用。尤其是,如果应用程序可能被嵌套在iframe中,那么只有在鼠标移动到iframe上时,鼠标事件才会被触发。
除此之外,我无法想到其他的方法,除非进行浏览器嗅探或测试屏幕分辨率并推断设备的可能能力。
还有一个创造性的想法!由于触摸浏览器中的鼠标事件是合成的,所以实现起来可能会更加复杂,但这是一个好主意!
这种方法可能适用于应用程序在启动时显示一个闪屏和一个“继续”按钮的情况。如果在第一个mousedown事件之前鼠标移动,那么可以判断设备上有鼠标。只有当按钮直接加载在鼠标下方且用户手持稳定时,才会失败(即使移动1像素也应该被捕捉到)。
很好的想法,但是根据我们的测试结果,似乎无法正常工作。在iPad上会触发这个事件。
那你在你的情况下最终采取了什么措施呢?
要知道是鼠标还是触摸设备,还必须钩取触摸事件并阻止其默认处理程序。因此,首先响应的设备将是你要寻找的设备。代码如下:
window.document.body.addEventListener('mousemove', function (e) { e.preventDefault(); e.stopImmediatePropagation(); }, true);
iPad确实会触发mousemove事件,就在mousedown事件之前。我发现mousedown计数 > 0且mousedown计数 == mousemove计数是一种检测没有鼠标的好方法。但我无法用真正的鼠标重现这个问题。
只需检测touchstart事件以取消mousemove事件的监听器。这样,mousemove事件监听器在移动设备上将永远不会被触发。
你的建议在具有触摸屏和鼠标的设备上无法工作。
我是说检测touchstart事件,而不是触摸功能。如果访问者正在使用双输入设备,如果首先发生触摸交互,那么访问者很可能会继续使用触摸(即使存在鼠标)。如果在任何触摸事件之前发生鼠标移动,访问者很可能会继续使用鼠标。唯一可能破坏这一点的方式是访问者在同一个浏览会话中交替使用触摸屏和鼠标与网站进行交互。因此,不仅应该逐步使用这种方法。
除了原始的问题,你还可以添加对触摸和鼠标事件的监听器(指的是监听实际事件,而不是设备的触摸功能)。当它们发生时,你就知道它们是存在的。然而,正如你所提到的,通过检测触摸或触摸事件,无法排除访问者是否有鼠标。只有通过检测特定设备的实际使用情况,才有可能确定访问者将继续使用同一输入方式,即双输入设备。
你一定是在回复caffinatedmonkey,因为我认为我们的观点几乎是一样的:钩取触摸和鼠标事件,看哪个首先触发,然后坚持使用那个。
对,抱歉!:D
iPad、iPhone和Android在触摸事件上会触发mousemove事件,但是你可以通过监听触摸事件并调用event.preventDefault()来避免这种行为。这样,如果mousemove事件被触发,那么它就是一个真实的鼠标事件。
检测浏览器是否没有鼠标且仅支持触摸的问题出现的原因是因为不同的设备/用例有以下几类:
1. 鼠标和键盘(桌面)
2. 仅支持触摸(手机/平板)
3. 鼠标、键盘和触摸(触摸笔记本电脑)
4. 触摸和键盘(平板上的蓝牙键盘)
5. 仅鼠标(残障用户/浏览偏好)
6. 仅键盘(残障用户/浏览偏好)
7. 触摸和鼠标(例如 Galaxy Note 2 笔触发的悬停事件)
更糟糕的是,用户可以从其中一类转换到另一类(插入鼠标,连接键盘),或者用户可能在触摸屏幕之前表现得就像是在正常的笔记本电脑上一样。
你的观点是正确的,假设浏览器中存在事件构造器并不是前进的好方法(而且有点不一致)。此外,除非你跟踪一个非常特定的事件或者仅仅试图排除上面几类中的一些,否则使用事件本身并不是完全可靠的。
例如,假设你已经发现用户发出了一个真实的鼠标移动事件(而不是来自触摸事件的虚假事件),然后呢?
你启用悬停样式?你添加更多按钮?
无论哪种方式,都会增加触发事件的时间。
但是,当你的用户决定拔下鼠标并完全触摸屏幕时,你会等他触摸到你现在拥挤的界面,然后在他努力找到你现在拥挤的用户界面后立即更改它吗?
总结 stucox 在 GitHub 上的回答:
- 我们想要检测鼠标的存在
- 我们可能无法在事件触发之前检测到它
- 因此,我们检测的是鼠标是否在此会话中被使用过 —— 它不会立即从页面加载时开始
- 我们可能也无法检测是否没有鼠标 —— 直到鼠标真实存在之前都是未定义的(我认为这比将其设置为假直到证明为真更有意义)
- 我们可能也无法检测是否在会话中断开了鼠标连接 —— 这与用户放弃使用鼠标是无法区分的
顺带一提:浏览器确实知道用户何时插入鼠标/连接键盘,但是不会将其公开给 JavaScript.. 真可惜!
这应该引导你思考以下问题:
- 跟踪给定用户的当前功能是复杂、不可靠且毫无意义的
- 渐进增强的思想在这里非常适用。构建一个无论用户的上下文如何都能平稳工作的体验。然后根据浏览器特性/媒体查询做出一些基于假设上下文的功能增强。鼠标的存在只是不同设备上不同用户体验你的网站的众多方式之一。创造一个有价值的核心,不要太过担心人们如何点击按钮。
很棒的答案。希望用户总是有屏幕!我认为构建一个能够根据用户当前交互模式自动调整的界面是有意义的。在触摸笔记本电脑上,当用户从鼠标切换到触摸时调整应用程序(例如 :hover
元素和类似的东西)是有意义的。目前看来,用户同时使用鼠标和触摸的可能性很小(我的意思是,这就像是在同一台计算机上连接了两个鼠标哈哈哈)
- 很抱歉打破你的幻想,但用户并不总是有屏幕。(是否可能使用 JavaScript 检测用户机器上是否正在运行屏幕阅读器?)
从2018年起,有一种可靠的方法可以检测浏览器是否有鼠标(或类似的输入设备):CSS4媒体交互特性,几乎所有现代浏览器都支持这种特性(除了IE 11和特殊的移动浏览器)。
W3C:
pointer媒体特性用于查询指针设备(如鼠标)的存在和准确性。
以下是一些选项:
/* 设备的主要输入机制包括准确性有限的指针设备。*/ (pointer: coarse) { ... } /* 设备的主要输入机制包括准确的指针设备。*/ (pointer: fine) { ... } /* 设备的主要输入机制不包括指针设备。*/ (pointer: none) { ... } /* 主要输入机制系统可以轻松悬停在元素上。*/ (hover: hover) { ... } /* 主要输入机制根本无法悬停,或者无法方便地悬停(例如,许多移动设备在用户执行不方便的长按时模拟悬停),或者没有主要的指向输入机制。*/ (hover: none) { ... } /* 一个或多个可用的输入机制可以轻松悬停在元素上。*/ (any-hover: hover) { ... } /* 一个或多个可用的输入机制无法悬停(或者没有指向输入机制)。*/ (any-hover: none) { ... }
媒体查询也可以在JS中使用:
if(window.matchMedia("(any-hover: none)").matches) { // do something }
相关链接:
W3文档:https://www.w3.org/TR/mediaqueries-4/#mf-interaction
浏览器支持:https://caniuse.com/#search=media%20features
类似问题:Detect if a client device supports :hover and :focus states
个人而言,我喜欢这个答案,但截至目前(10/19),根据caniuse.com的数据,全球约85%的设备支持悬停和指针CSS查询。当然,95%或更高的支持率更好。希望这种特性很快在设备上成为标准。
基本上,我同意你的批评,这个特性还没有得到普遍支持。但实际上,除了IE 11和移动设备上的特殊浏览器之外,它在几乎所有地方都得到了支持。为了公平起见,这对于任何现代功能来说都是真实的,因为微软早就停止实现IE功能了。对于IE 11,你可以使用其他答案中的备用方法。
2020年9月:我在安卓智能手机上尝试使用媒体匹配(hover: hover),它匹配成功,但在w3链接中说它不应该匹配成功。
如果W3说不应该匹配成功,那就不应该匹配成功。你使用的是哪个浏览器?
Chrome 85.0.4183.101,安卓8.1.0 Aquaris V Build。不过我找到了解决办法,所以没有深入研究。
window.matchMedia("(any-pointer: fine)").matches
在我的所有移动浏览器和桌面浏览器上都返回true,原因不明。window.matchMedia("(any-hover: hover)").matches
在没有鼠标的移动设备上始终返回true。只有window.matchMedia("(any-pointer: coarse)").matches
在移动设备上返回true,在桌面上返回false,但它不考虑连接的鼠标或S-Pen。
这篇文章从实际角度详细描述了这些特性:css-tricks.com/touch-devices-not-judged-size
无论是在桌面上的任何Chromium浏览器上,window.matchMedia("(any-hover: none)").matches
始终返回false,而在触摸笔记本上的任何Chromium浏览器上,window.matchMedia("(any-pointer: fine)").matches
始终返回false,所以尽管caniuse.com说这个方法有效,但在Chromium浏览器中它绝对不能正确返回值!
我建议你阅读CSS tricks上的以下文章:css-tricks.com/…
这篇文章很好,但它也没有提到,即使你理解如何使用它,在基于Chromium的浏览器中这个解决方案仍然不起作用,因为Windows上的Chromium浏览器的媒体查询根本不能正常工作。只需在任何一个你选择的Chromium浏览器(Chrome、Vivaldi、Iron、Edge)中的开发者工具中执行window.matchMedia("(any-hover: none)").matches
,Firefox是唯一完全支持“CSS4媒体交互”特性的浏览器。
如果在基于Chromium的浏览器中还不能完全按预期工作,他们希望能够实现它。它仍然是一个候选推荐草案,尚未成为标准(w3.org/TR/mediaqueries-4/#mf-interaction)。尽管在我的使用场景中它都起作用,但我尚未在实践中使用过任何变种。