Javascript 检测到浏览器没有鼠标并且只能触摸
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7838680/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Detecting that the browser has no mouse and is touch-only
提问by nraynaud
I'm developing a webapp (not a website with pages of interesting text) with a very different interface for touch (your finger hides the screen when you click) and mouse (relies heavily on hover preview). How can I detect that my user has no mouse to present him the right interface? I plan to leave a switch for people with both mouse and touch (like some notebooks).
我正在开发一个 web 应用程序(不是一个包含有趣文本页面的网站),它有一个非常不同的触摸界面(当你点击时你的手指隐藏屏幕)和鼠标(严重依赖悬停预览)。如何检测我的用户没有鼠标来向他展示正确的界面?我打算为同时使用鼠标和触摸的人(如某些笔记本电脑)留下一个开关。
The touch event capability in the browser doesn't actually mean the user is using a touch device (for example, Modernizr doesn't cut it). The code that correctly answers the question should return false if the device has a mouse, true otherwise. For devices with mouse and touch, it should return false (not touch only)
浏览器中的触摸事件功能实际上并不意味着用户正在使用触摸设备(例如,Modernizr 并没有削减它)。如果设备有鼠标,正确回答问题的代码应该返回 false,否则返回 true。对于带有鼠标和触摸的设备,它应该返回 false(不仅仅是触摸)
As a side note, my touch interface might also be suitable for keyboard-only devices, so it's more the lack of mouse I'm looking to detect.
附带说明一下,我的触摸界面可能也适用于仅使用键盘的设备,因此我更希望检测到缺少鼠标。
To make the need more clear, here is the API that I'm looking to implement:
为了更清楚地说明需求,这里是我希望实现的 API:
// Level 1
// The current answers provide a way to do that.
hasTouch();
// Returns true if a mouse is expected.
// Note: as explained by the OP, this is not !hasTouch()
// I don't think we have this in the answers already, that why I offer a bounty
hasMouse();
// Level 2 (I don't think it's possible, but maybe I'm wrong, so why not asking)
// callback is called when the result of "hasTouch()" changes.
listenHasTouchChanges(callback);
// callback is called when the result of "hasMouse()" changes.
listenHasMouseChanges(callback);
回答by Wyatt
The main trouble is that you have the following different classes of devices/use cases:
主要的问题是您有以下不同类别的设备/用例:
- Mouse and keyboad (desktop)
- Touch only (phone/tablet)
- Mouse, keyboard, and touch (touch laptops)
- Touch and keyboard (bluetooth keyboard on tablet)
- Mouse only (Disabled user/browsing preference)
- Keyboard only (Disabled user/browsing preference)
- Touch and mouse (ie hover events from Galaxy Note 2 pen)
- 鼠标和键盘(桌面)
- 仅触摸(手机/平板电脑)
- 鼠标、键盘和触控(触控笔记本电脑)
- 触摸和键盘(平板电脑上的蓝牙键盘)
- 仅鼠标(禁用用户/浏览偏好)
- 仅键盘(禁用用户/浏览偏好)
- 触摸和鼠标(即来自 Galaxy Note 2 笔的悬停事件)
What's worse, is that one can transition from some of these classes to others (plugs in a mouse, connects to keyboard), or a user may APPEAR to be on a normal laptop until they reach out and touch the screen.
更糟糕的是,人们可以从这些类中的一些过渡到其他类(插入鼠标,连接到键盘),或者用户可能看起来像在普通笔记本电脑上,直到他们伸出手触摸屏幕。
You are correct in assuming that the presence of event constructors in the browser is not a good way to move forward (and it is somewhat inconsistent). Additionally, unless you are tracking a very specific event or only trying to rule out a few classes above, using events themselves isn't full proof.
您假设浏览器中存在事件构造函数不是前进的好方法(并且有些不一致),这是正确的。此外,除非您正在跟踪一个非常具体的事件或只是试图排除上述几个类,否则使用事件本身并不是充分的证据。
For example, say you've discovered that a user have emitted a real mousemove (not the false one from touch events, see http://www.html5rocks.com/en/mobile/touchandmouse/).
例如,假设您发现用户发出了一个真正的 mousemove(不是来自触摸事件的错误,请参阅http://www.html5rocks.com/en/mobile/touchandmouse/)。
Then what?
然后呢?
You enable hover styles? You add more buttons?
你启用悬停样式?你添加更多按钮?
Either way you are increasing time to glass because you have to wait for an event to fire.
无论哪种方式,您都在增加玻璃的时间,因为您必须等待事件触发。
But then what happens when your noble user decides wants to unplug his mouse and go full touch.. do you wait for him to touch your now crammed interface, then change it right after he's made the effort to pinpoint your now crowded UI?
但是,当您的高贵用户决定要拔掉他的鼠标并完全触摸时会发生什么..您是否等待他触摸您现在塞满的界面,然后在他努力查明您现在拥挤的用户界面后立即更改它?
In bullet form, quoting stucox at https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101
以项目符号形式,在https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101引用 stucox
- We want to detect the presence of a mouse
- Ae probably can't detect before an event is fired
- As such, what we're detecting is if a mouse has been used in this session — it won't be immediately from page load
- We probably also can't detect that there isn't a mouse — it'd be undefined until true (I think this makes more sense than setting it false until proven)
- And we probably can't detect if a mouse is disconnected mid-session — that'll be indistinguishable from the user just giving up with their mouse
- 我们想检测鼠标的存在
- 在触发事件之前,Ae 可能无法检测到
- 因此,我们正在检测的是,如果在此会话中使用了鼠标——它不会立即从页面加载
- 我们可能也无法检测到没有鼠标——它在为真之前是未定义的(我认为这比在证明之前将其设置为假更有意义)
- 而且我们可能无法检测到鼠标是否在会话期间断开连接——这与用户只是放弃鼠标无法区分
An aside: the browser DOES know when a user plugs in a mouse/connects to a keyboard, but doesn't expose it to JavaScript.. dang!
旁白:浏览器确实知道用户何时插入鼠标/连接到键盘,但不会将其暴露给 JavaScript .. dang!
This should lead you to the following:
这应该引导您到以下几点:
Tracking the current capabilities of a given user is complex, unreliable, and of dubious merit
跟踪给定用户的当前能力是复杂的、不可靠的并且值得怀疑
The idea of progressive enhancement applies quite well here, though. Build an experience that works smoothly no matter the context of the user. Then make assumptions based on browser features/media queries to add functionality that will be relative in the assumed context. Presence of a mouse is just one of the multitudes of ways in which different users on different devices experience your website. Create something with merit at its kernel and don't worry too much about how people click the buttons.
不过,渐进增强的想法在这里非常适用。无论用户所处的环境如何,都能打造流畅运行的体验。然后根据浏览器功能/媒体查询进行假设,以添加在假设上下文中相对的功能。鼠标的存在只是不同设备上的不同用户体验您的网站的众多方式之一。在其核心创造一些有价值的东西,不要太担心人们如何点击按钮。
回答by Dan
How about listening for a mousemove event on the document. Then until you hear that event you assume that the device is touch or keyboard only.
如何监听文档上的 mousemove 事件。然后,直到您听到该事件,您才假定该设备仅是触摸式或键盘式的。
var mouseDetected = false;
function onMouseMove(e) {
unlisten('mousemove', onMouseMove, false);
mouseDetected = true;
// initializeMouseBehavior();
}
listen('mousemove', onMouseMove, false);
(Where listen
and unlisten
delegate to addEventListener
or attachEvent
as appropriate.)
(在哪里listen
和unlisten
委托给addEventListener
或attachEvent
酌情。)
Hopefully this wouldn't lead to too much visual jank, it would suck if you need massive re-layouts based on mode...
希望这不会导致太多的视觉卡顿,如果您需要基于模式进行大量重新布局,那会很糟糕……
回答by Blackbam
As of 2018 there is a good and reliable way to detect if a browser has a mouse (or similar input device): CSS4 media interaction featureswhich are now supported by almost any modern browser (except IE 11 and special mobile browsers).
截至 2018 年,有一种很好且可靠的方法来检测浏览器是否有鼠标(或类似的输入设备):CSS4 媒体交互功能现在几乎所有现代浏览器(IE 11 和特殊移动浏览器除外)都支持。
W3C:
W3C:
The pointer media feature is used to query the presence and accuracy of a pointing device such as a mouse.
指针媒体功能用于查询指针设备(如鼠标)的存在和准确性。
See the following options:
请参阅以下选项:
/* The primary input mechanism of the device includes a
pointing device of limited accuracy. */
@media (pointer: coarse) { ... }
/* The primary input mechanism of the device
includes an accurate pointing device. */
@media (pointer: fine) { ... }
/* The primary input mechanism of the
device does not include a pointing device. */
@media (pointer: none) { ... }
/* Primary input mechanism system can
hover over elements with ease */
@media (hover: hover) { ... }
/* Primary input mechanism cannot hover
at all or cannot conveniently hover
(e.g., many mobile devices emulate hovering
when the user performs an inconvenient long tap),
or there is no primary pointing input mechanism */
@media (hover: none) { ... }
/* One or more available input mechanism(s)
can hover over elements with ease */
@media (any-hover: hover) { ... }
/* One or more available input mechanism(s) cannot
hover (or there are no pointing input mechanisms) */
@media (any-hover: none) { ... }
Media queries can also be used in JS:
媒体查询也可以在 JS 中使用:
if(window.matchMedia("(any-hover: none)").matches) {
// do sth
}
Related:
有关的:
Official working draft: https://drafts.csswg.org/mediaqueries/#mf-interaction
官方工作草案:https: //drafts.csswg.org/mediaqueries/#mf-interaction
Browser support: https://caniuse.com/#search=media%20features
浏览器支持:https: //caniuse.com/#search=media%20features
Similar problem: Detect if a client device supports :hover and :focus states
回答by Hugo Silva
@Wyatt's answer is great and gives us a lot to think about.
@Wyatt 的回答很棒,给了我们很多思考。
On my case, I chose to listen for the first interaction, to only then set a behavior. So, even if the user has a mouse, I will treat as touch device if first interaction was a touch.
就我而言,我选择监听第一次交互,然后才设置行为。因此,即使用户有鼠标,如果第一次交互是触摸,我也会将其视为触摸设备。
Considering the given order in which events are processed:
- touchstart
- touchmove
- touchend
- mouseover
- mousemove
- mousedown
- mouseup
- click
- 触摸启动
- 触摸移动
- 触摸端
- 鼠标移到
- 鼠标移动
- 鼠标按下
- 鼠标向上
- 点击
We can assume that if mouse event gets triggered before touch, it is a real mouse event, not an emulated one. Example (using jQuery):
我们可以假设,如果鼠标事件在触摸之前被触发,则它是一个真正的鼠标事件,而不是模拟事件。示例(使用 jQuery):
$(document).ready(function() {
var $body = $('body');
var detectMouse = function(e){
if (e.type === 'mousedown') {
alert('Mouse interaction!');
}
else if (e.type === 'touchstart') {
alert('Touch interaction!');
}
// remove event bindings, so it only runs once
$body.off('mousedown touchstart', detectMouse);
}
// attach both events to body
$body.on('mousedown touchstart', detectMouse);
});
That worked for me
那对我有用
回答by Hugo Silva
It's only possible to detect if a browser is touch capable. There is no way to know if it actually has a touch screen or a mouse connected.
这是唯一可以检测,如果浏览器是触摸能力。没有办法知道它是否真的连接了触摸屏或鼠标。
One can prioritize the use though by listening to touch event instead of mouse event if touch capability is detected.
如果检测到触摸功能,则可以通过侦听触摸事件而不是鼠标事件来优先使用。
To detect touch capability cross-browser:
跨浏览器检测触摸功能:
function hasTouch() {
return (('ontouchstart' in window) || // html5 browsers
(navigator.maxTouchPoints > 0) || // future IE
(navigator.msMaxTouchPoints > 0)); // current IE10
}
Then one can use this to check:
然后可以使用它来检查:
if (!hasTouch()) alert('Sorry, need touch!);
or to choose which event to listen to, either:
或者选择要收听的事件,或者:
var eventName = hasTouch() ? 'touchend' : 'click';
someElement.addEventListener(eventName , handlerFunction, false);
or use separate approaches for touch vs. non-touch:
或使用单独的方法进行触摸与非触摸:
if (hasTouch() === true) {
someElement.addEventListener('touchend' , touchHandler, false);
} else {
someElement.addEventListener('click' , mouseHandler, false);
}
function touchHandler(e) {
/// stop event somehow
e.stopPropagation();
e.preventDefault();
window.event.cancelBubble = true;
// ...
return false; // :-)
}
function mouseHandler(e) {
// sorry, touch only - or - do something useful and non-restrictive for user
}
For mouse one can only detect if the mouse is being used, not if it exists or not. One can setup a global flag to indicate that mouse was detected by usage (similar to an existing answer, but simplified a bit):
对于鼠标,只能检测鼠标是否正在使用,而不能检测它是否存在。可以设置一个全局标志来指示鼠标被使用检测到(类似于现有的答案,但稍微简化了一点):
var hasMouse = false;
window.onmousemove = function() {
hasMouse = true;
}
(one cannot include mouseup
or mousedown
as these event can also be triggered by touch)
(不能包含mouseup
或mousedown
因为这些事件也可以通过触摸触发)
Browsers restricts access to low-level system APIs which is needed to be able to detect features such as hardware capabilities of the system it's being used on.
浏览器限制对低级系统 API 的访问,这些 API 需要能够检测正在使用它的系统的硬件功能等功能。
There is the possibility to perhaps write a plugin/extension to access these but via JavaScript and DOM such detection is limited for this purpose and one would have to write a plugin specific for the various OS platforms.
有可能编写一个插件/扩展来访问这些,但通过 JavaScript 和 DOM 这种检测仅限于此目的,并且必须为各种操作系统平台编写特定的插件。
So in conclusion: such detection can only be estimated by a "good guess".
所以总而言之:这种检测只能通过“好的猜测”来估计。
回答by joeytwiddle
When Media Queries Level 4is available in browsers, we will be able to use the "pointer" and "hover" queries to detect devices with a mouse.
当媒体查询级别 4在浏览器中可用时,我们将能够使用“指针”和“悬停”查询来检测带有鼠标的设备。
If we really want to communicate that information to Javascript, we could use a CSS query to set specific styles according to the device type, and then use getComputedStyle
in Javascript to read that style, and derive the original device type from it.
如果我们真的想将该信息传达给 Javascript,我们可以使用 CSS 查询根据设备类型设置特定样式,然后getComputedStyle
在 Javascript 中使用该样式读取该样式,并从中导出原始设备类型。
But a mouse can be connectedor unpluggedat any time, and the user may be wanting to switch between touch and mouse. So we may need to detectthis change, and offer to change interface or do so automatically.
但是鼠标可以随时连接或拔出,用户可能想要在触摸和鼠标之间切换。所以我们可能需要检测这种变化,并提供改变界面或自动这样做。
回答by Brandan
Since you're planning to offer a way to switch between the interfaces anyway, would it be feasible to simply ask the user to click a link or a button to "enter" the correct version of the application? Then you could remember their preference for future visits. It's not high-tech, but it's 100% reliable :-)
既然您计划提供一种在界面之间切换的方法,那么简单地要求用户单击链接或按钮以“输入”应用程序的正确版本是否可行?然后你可以记住他们对未来访问的偏好。它不是高科技,但它是 100% 可靠的 :-)
回答by daredev
@SamuelRossille No browsers that I'm aware of expose the existence of (or lack thereof) a mouse, unfortunately.
@SamuelRossille 不幸的是,我知道没有浏览器会暴露鼠标的存在(或不存在)。
So, with that being said, we just have to try and do the best we can with our existing option... events. I know it's not exactly what you're looking for... agreed it is currently far from ideal.
因此,话虽如此,我们只需要尝试使用现有选项尽我们所能……事件。我知道这不完全是你要找的......同意它目前远非理想。
We can do our best to figure out whether a user is using a mouse or touch at any given moment. Here is a quick and dirty example using jQuery & Knockout:
我们可以尽最大努力弄清楚用户在任何给定时刻是使用鼠标还是触摸。这是一个使用 jQuery 和 Knockout 的快速而肮脏的示例:
//namespace
window.ns = {};
// for starters, we'll briefly assume if touch exists, they are using it - default behavior
ns.usingTouch = ko.observable(Modernizr.touch); //using Modernizr here for brevity. Substitute any touch detection method you desire
// now, let's sort out the mouse
ns.usingMouse = ko.computed(function () {
//touch
if (ns.usingTouch()) {
//first, kill the base mousemove event
//I really wish browsers would stop trying to handle this within touch events in the first place
window.document.body.addEventListener('mousemove', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
}, true);
//remove mouse class from body
$('body').removeClass("mouse");
//switch off touch listener
$(document).off(".ns-touch");
// switch on a mouse listener
$(document).on('mousemove.ns-mouse', function (e) {
if (Math.abs(window.lastX - e.clientX) > 0 || window.lastY !== e.clientY) {
ns.usingTouch(false); //this will trigger re-evaluation of ns.usingMouse() and result in ns.usingMouse() === true
}
});
return false;
}
//mouse
else {
//add mouse class to body for styling
$('body').addClass("mouse");
//switch off mouse listener
$(document).off(".ns-mouse");
//switch on a touch listener
$(document).on('touchstart.ns-touch', function () { ns.usingTouch(true) });
return true;
}
});
//tests:
//ns.usingMouse()
//$('body').hasClass('mouse');
You can now bind/subscribe to usingMouse()& usingTouch()and/or style your interface with the body.mouseclass. The interface will switch back and forth as soon as a mouse cursor is detected and on touchstart.
您现在可以绑定/订阅 usingMouse()和usingTouch()和/或使用body.mouse类设置界面样式。一旦检测到鼠标光标并在 touchstart 上,界面就会来回切换。
Hopefully we'll have some better options from the browser vendors soon.
希望我们很快就会从浏览器供应商那里获得一些更好的选择。
回答by iblue
Why don't you just detect if it has the ability to sense touches and/or to react to mouse movements?
为什么不检测它是否具有感知触摸和/或对鼠标移动做出反应的能力?
// This will also return false on
// touch-enabled browsers like Chrome
function has_touch() {
return !!('ontouchstart' in window);
}
function has_mouse() {
return !!('onmousemove' in window);
}
回答by Gigi
Tera-WURFLcan tell you the capabilities of the device that is visiting your site by comparing the browser signatureagainst its database. Give it a look, its free!
Tera-WURFL可以通过将浏览器签名与其数据库进行比较来告诉您访问您站点的设备的功能。看一看,它是免费的!