检测到浏览器没有鼠标且仅触摸

nra*_*aud 140 javascript mouse html5 touch

我正在开发一个webapp(不是一个有趣文本页面的网站),它有一个非常不同的触摸界面(当你点击时你的手指隐藏屏幕)和鼠标(严重依赖于悬停预览).如何检测到我的用户没有鼠标向他显示正确的界面?我计划为鼠标和触摸的人留下一个开关(就像一些笔记本电脑).

浏览器中的触摸事件功能实际上并不意味着用户正在使用触摸设备(例如,Modernizr不会剪切它).如果设备有鼠标,正确回答问题的代码应该返回false,否则返回true.对于具有鼠标和触摸功能的设备,它应该返回false(不是仅触摸)

作为旁注,我的触摸界面可能也适用于仅限键盘的设备,因此更多的是缺少我想要检测的鼠标.

为了使需求更加清晰,以下是我要实现的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);
Run Code Online (Sandbox Code Playgroud)

Wya*_*att 59

主要问题是您有以下不同类别的设备/用例:

  1. 鼠标和键盘(桌面)
  2. 仅触摸(手机/平板电脑)
  3. 鼠标,键盘和触控(触控笔记本电脑)
  4. 触摸和键盘(平板电脑上的蓝牙键盘)
  5. 仅鼠标(禁用用户/浏览首选项)
  6. 仅键盘(禁用用户/浏览首选项)
  7. 触摸和鼠标(即来自Galaxy Note 2笔的悬停事件)

更糟糕的是,人们可以从其中一些类转换到其他类(插入鼠标,连接到键盘),或者用户可以在正常的笔记本电脑上显示,直到它们伸出并触摸屏幕.

假设浏览器中存在事件构造函数不是向前推进的好方法(并且它有些不一致),这是正确的.此外,除非您正在跟踪特定事件或仅尝试排除上述几个类,否则使用事件本身并不是完全证明.

例如,假设您发现用户已发出真正的鼠标移动(而不是触摸事件中的错误移动,请参阅http://www.html5rocks.com/en/mobile/touchandmouse/).

那又怎样?

你启用悬停样式?你添加更多按钮?

无论哪种方式,你都要增加玻璃时间,因为你必须等待事件发生.

但是当你的高贵用户决定拔掉他的鼠标并完全触摸时会发生什么...你是否等待他触摸你现在挤满的界面,然后在他努力确定你现在拥挤的用户界面之后立即改变它?

在子弹形式,引用stucox在https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101

  • 我们想要检测鼠标的存在
  • 在事件发生之前,Ae可能无法检测到
  • 因此,我们正在检测的是,如果在此会话中使用了鼠标 - 它将不会立即从页面加载
  • 我们可能也无法检测到没有鼠标 - 它是真的未定义(我认为这比设置它更有意义,直到证明)
  • 而且我们可能无法检测鼠标是否在会话期间断开连接 - 这与用户放弃鼠标无法区分

抛开:浏览器知道用户何时插入鼠标/连接键盘,但不会将其暴露给JavaScript ..当然!

这应该会引导您进行以下操作:

跟踪给定用户的当前能力是复杂的,不可靠的,并且具有可疑的优点

但是,渐进式增强的想法在这里很适用.无论用户的上下文如何,都可以构建一种平稳运行的体验.然后根据浏览器功能/媒体查询进行假设,以添加在假定上下文中相对的功能.鼠标的存在只是不同设备上不同用户体验您网站的众多方式之一.在内核中创建具有优点的东西,不要过于担心人们如何点击按钮.

  • 很好的答案。希望用户总是有一个屏幕!我认为构建一个适合用户当前交互模式的界面是有意义的。在触摸笔记本电脑上,当用户从鼠标切换到触摸时,调整应用程序(即`:hover` 元素和类似的东西)是有意义的。用户目前似乎不太可能同时使用鼠标 + 触摸(我的意思是 comoonn 就像将 2 个鼠标连接到同一台计算机一样哈哈哈) (3认同)

Dan*_*Dan 55

如何在文档上监听mousemove事件.然后,直到您听到该事件,您才认为该设备仅为触摸或键盘.

var mouseDetected = false;
function onMouseMove(e) {
  unlisten('mousemove', onMouseMove, false);
  mouseDetected = true;
  // initializeMouseBehavior();
}
listen('mousemove', onMouseMove, false);
Run Code Online (Sandbox Code Playgroud)

(在何处listen,unlisten代表addEventListenerattachEvent酌情委托.)

希望这不会导致太多的视觉冲击,如果您需要基于模式的大量重新布局,它会很糟糕...

  • 好主意,但**似乎不适用于我们的测试**.iPad触发此事件. (47认同)
  • 如果应用程序以启动画面和"继续"按钮启动,则可以使用此功能.如果鼠标在第一个mousedown事件之前移动,那么你有一个鼠标.如果按钮直接加载到鼠标下并且用户手上非常稳定(即使应该移动1个像素),它也只会失败. (7认同)
  • @JeffAtwood你最终在你的案件中做了什么? (3认同)
  • 这是一个好主意,但不幸的是,当应用程序的UI取决于鼠标是否可用时,响应的延迟将使其无法使用..如果应用程序可能是iframed,则尤其如此,因此只有当鼠标事件发生时才会触及它鼠标移动到iframe本身.. (2认同)
  • 在mousedown活动之前,iPad肯定会触发mousemove活动.我发现mousedown count> 0并且mousedown count == mousemove count是检测无鼠标的好方法.我不能用真正的鼠标复制它. (2认同)

Hug*_*lva 23

@ Wyatt的答案很棒,给了我们很多思考.

在我的情况下,我选择听第一次互动,然后才设置一个行为.因此,即使用户有鼠标,如果第一次互动是触摸,我将视为触摸设备.

考虑处理事件给定顺序:

  1. touchstart
  2. touchmove
  3. touchend
  4. 鼠标移到
  5. 鼠标移动
  6. 鼠标按下
  7. 鼠标松开
  8. 点击

我们可以假设如果在触摸之前触发鼠标事件,则它是真正的鼠标事件,而不是模拟事件.示例(使用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);
});
Run Code Online (Sandbox Code Playgroud)

这对我有用

  • @netzaffin.感谢您的反馈,我发现使用mousedown而不是mouseover更加一致.你能从你的IOS看看这个小提琴,让我知道结果吗?干杯https://jsfiddle.net/bkwb0qen/15/embedded/result/ (3认同)

Bla*_*bam 21

截至2018年,有一种良好可靠的方法可以检测浏览器是否有鼠标(或类似的输入设备):CSS4媒体交互功能已被大多数现代浏览器支持(其余部分即将推出).

W3C:

指针媒体特征用于查询诸如鼠标的指示设备的存在和准确性.

请参阅以下选项:

    /* 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) { ... }
Run Code Online (Sandbox Code Playgroud)

媒体查询也可以在JS中使用:

if(window.matchMedia("(any-hover: none)").matches) {
    // do sth
}
Run Code Online (Sandbox Code Playgroud)

有关:

https://drafts.c​​sswg.org/mediaqueries/#mf-interaction

检测客户端设备是否支持:hover和:focus状态

  • @MQuiggGeorgia 基本上我基本上同意你的批评,它还没有得到所有地方的支持。caniuse.com 对我来说仍然表示支持 91.2% (https://caniuse.com/#feat=css-media-interaction)。仔细看看,除了 IE 11 和移动设备上的特殊扁平化浏览器之外,它在所有地方都受支持。公平地说,这对于任何现代功能都是如此,因为微软很久以前就停止实现 IE 功能了。对于 IE 11,您可以使用此处其他答案的后备。 (2认同)

小智 9

只能检测浏览器是否具有触控功能.没有办法知道它是否实际上有触摸屏或鼠标连接.

如果检测到触摸能力,则可以通过监听触摸事件而不是鼠标事件来优先使用.

要检测跨浏览器的触摸功能:

function hasTouch() {
    return (('ontouchstart' in window) ||       // html5 browsers
            (navigator.maxTouchPoints > 0) ||   // future IE
            (navigator.msMaxTouchPoints > 0));  // current IE10
}
Run Code Online (Sandbox Code Playgroud)

然后可以用它来检查:

if (!hasTouch()) alert('Sorry, need touch!);
Run Code Online (Sandbox Code Playgroud)

或选择要收听的事件:

var eventName = hasTouch() ? 'touchend' : 'click';
someElement.addEventListener(eventName , handlerFunction, false);
Run Code Online (Sandbox Code Playgroud)

或使用单独的触摸与非触摸方法:

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
}
Run Code Online (Sandbox Code Playgroud)

对于鼠标,只能检测鼠标是否被使用,而不是它是否存在.可以设置一个全局标志来指示鼠标是否被使用检测到(类似于现有的答案,但简化了一点):

var hasMouse = false;

window.onmousemove = function() {
    hasMouse = true;
}
Run Code Online (Sandbox Code Playgroud)

(一个不能包含mouseupmousedown因为这些事件也可以通过触摸触发)

浏览器限制对低级系统API的访问,这些API需要能够检测诸如正在使用的系统的硬件功能之类的功能.

有可能编写插件/扩展来访问这些,但是通过JavaScript和DOM,这样的检测仅限于此目的,并且必须编写专用于各种OS平台的插件.

总而言之:这种检测只能通过"好猜测"来估算.


joe*_*dle 8

Media Queries Level 4在浏览器中可用时,我们将能够使用"指针"和"悬停"查询来使用鼠标检测设备.

如果我们真的想要将这些信息传达给Javascript,我们可以使用CSS查询根据设备类型设置特定样式,然后使用getComputedStyleJavascript来读取该样式,并从中派生原始设备类型.

但是可以随时连接拔出鼠标,用户可能想要在触摸和鼠标之间切换.因此,我们可能需要检测此更改,并提供更改界面或自动执行此操作.

  • window.matchMedia("(any-pointer:coarse)").匹配=== true? (2认同)

Bra*_*dan 6

既然您计划提供一种在接口之间切换的方法,那么只需要用户点击链接或按钮"输入"正确版本的应用程序是否可行?然后你可以记住他们对未来访问的偏好.它不是高科技,但它100%可靠:-)

  • 这实际上是一个非常好的建议,但它延迟了用户进入真实界面之前的时间.此外,我必须在初始选择后提供切换方式.与简单地检测到相比,结束更多的工作. (2认同)

小智 6

这在类似的情况下对我有用。基本上,假设用户没有鼠标,直到您看到一系列短的连续鼠标移动,而无需干预鼠标按下或鼠标松开。不是很优雅,但是很有效。

var mousedown = false;
var mousemovecount = 0;
function onMouseDown(e){
    mousemovecount = 0;
    mousedown = true;
}
function onMouseUp(e){
    mousedown = false;
    mousemovecount = 0;
}
function onMouseMove(e) {
    if(!mousedown) {
        mousemovecount++;
        if(mousemovecount > 5){
            window.removeEventListener('mousemove', onMouseMove, false);
            console.log("mouse moved");
            $('body').addClass('has-mouse');
        }
    } else {
        mousemovecount = 0;
    }
}
window.addEventListener('mousemove', onMouseMove, false);
window.addEventListener('mousedown', onMouseDown, false);
window.addEventListener('mouseup', onMouseUp, false);
Run Code Online (Sandbox Code Playgroud)


Mar*_*ope -7

通常,检测是否支持鼠标悬停功能比检测操作系统/浏览器类型更好。您可以简单地通过以下操作来做到这一点:

if (element.mouseover) {
    //Supports the mouseover event
}
Run Code Online (Sandbox Code Playgroud)

确保您执行以下操作:

if (element.mouseover()) {
    // Supports the mouseover event
}
Run Code Online (Sandbox Code Playgroud)

后者实际上会调用该方法,而不是检查它是否存在。

您可以在这里阅读更多信息: http: //www.quirksmode.org/js/support.html

  • 我真的不知道从你的帖子中得到什么,但是 if ('onmouseover' in $('body')[0])alert('onmouseover'); iPhone 中也会显示一条消息 (2认同)

归档时间:

查看次数:

42157 次

最近记录:

5 年,10 月 前