【问题标题】:Detecting that the browser has no mouse and is touch-only检测浏览器没有鼠标并且是仅触摸的
【发布时间】:2011-12-11 22:14:36
【问题描述】:

我正在开发一个 web 应用程序(不是一个包含有趣文本页面的网站),它具有非常不同的触摸界面(单击时手指会隐藏屏幕)和鼠标(严重依赖悬停预览)。 我怎样才能检测到我的用户没有鼠标来向他展示正确的界面?我打算为同时使用鼠标和触摸的人(如某些笔记本)留下一个开关。

浏览器中的触摸事件功能实际上并不意味着用户正在使用触摸设备(例如,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);

【问题讨论】:

  • 我认为,如果您希望一个应用程序同时适用于桌面和移动/触控,但每个应用程序具有不同的行为,您需要重新考虑您的设计。我认为此时您实际上不可能实现,因为在 Google 上快速搜索“javascript 检测鼠标”会显示 quirksmode.org 上的一篇相当有用的帖子,用于检测鼠标的各种状态(点击、位置、等),但鼠标是否实际存在的结果为零。
  • 也许是因为谷歌没有帮助我才在这里问的。
  • 你试过从 jquery 中输入鼠标输入吗? $(document).mouseenter(function(e) { alert("mouse"); });
  • 在考虑了近十种有希望的途径后,在几分钟内拒绝了每一种途径,这个问题让我非常疯狂。

标签: javascript html css touch mouse


【解决方案1】:

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

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

更糟糕的是,人们可以从其中一些类转换到其他类(插入鼠标、连接到键盘),或者用户可能会出现在普通笔记本电脑上,直到他们伸手触摸屏幕。

您正确地假设浏览器中存在事件构造函数不是前进的好方法(而且它有些不一致)。此外,除非您正在跟踪一个非常具体的事件或只是试图排除上述几个类别,否则使用事件本身并不能完全证明。

例如,假设您发现用户发出了真正的 mousemove(不是来自触摸事件的错误,请参阅 http://www.html5rocks.com/en/mobile/touchandmouse/)。

然后呢?

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

无论哪种方式,您都在增加玻璃杯的时间,因为您必须等待事件触发。

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

以项目符号的形式,在https://github.com/Modernizr/Modernizr/issues/869#issuecomment-15264101引用stucox

  • 我们想要检测鼠标的存在
  • Ae 可能无法在事件触发之前检测到
  • 因此,我们检测的是在此会话中是否使用了鼠标 - 不会立即从页面加载开始
  • 我们可能也无法检测到没有鼠标 - 在 true 之前它是未定义的(我认为这比设置更有意义 在被证明之前它是错误的)
  • 而且我们可能无法检测到鼠标是否在会话中断开 - 这与用户只是放弃他们的 鼠标

顺便说一句:浏览器确实知道用户何时插入鼠标/连接到键盘,但不会将其暴露给 JavaScript.. dang!

这应该会引导您到达以下位置:

跟踪给定用户的当前能力是复杂、不可靠且值得怀疑的

不过,渐进增强的想法在这里非常适用。无论用户所处的环境如何,都可以打造顺畅运行的体验。然后根据浏览器功能/媒体查询做出假设,以添加在假设上下文中相关的功能。鼠标的存在只是不同设备上的不同用户体验您的网站的众多方式之一。在内核中创建具有优点的东西,不要太担心人们如何点击按钮。

【讨论】:

  • 很好的答案。希望用户始终拥有屏幕!我认为构建一个适合当前用户交互模式的界面是有意义的。在触摸笔记本电脑上,当用户从鼠标切换到触摸时,调整应用程序(即:hover 元素和类似的东西)是有意义的。用户当前似乎不太可能同时使用鼠标 + 触摸(我的意思是 comoonn 就像将 2 个鼠标连接到同一台计算机哈哈哈)
  • @SebastienLorber - 不想把它告诉你,但用户必须总是有一个屏幕。 (Is it possible to use javascript to detect if a screen reader is running on a users machine?)
【解决方案2】:

截至 2018 年,有一种良好且可靠的方法可以检测浏览器是否有鼠标(或类似的输入设备):CSS4 媒体交互功能现在几乎所有现代浏览器都支持(除了IE 11 和特殊的移动浏览器)。

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) { ... }

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

if(window.matchMedia("(any-hover: none)").matches) {
    // do sth
}

相关:

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 的数据,@media hover 和 pointer CSS 查询仅在全球约 85% 的设备上可用。当然还不错,95%以上最好。希望这将很快成为设备上的标准。
  • @MQuiggGeorgia 基本上我同意你的批评,但它还不是到处都支持。我仍然 caniuse.com 说它支持 91.2% (caniuse.com/#feat=css-media-interaction)。仔细一看,除了 IE 11 和移动设备上的特殊扁平化浏览器外,它在所有地方都受支持。公平地说,任何现代功能都是如此,因为微软很久以前就停止了 IE 功能。对于 IE 11,您可以在此处使用其他答案的后备。
  • 2020 年 9 月:我正在 Android 智能手机中尝试媒体匹配(悬停:悬停),它匹配,而 w3 链接中说不应该
  • @raquelhortab 如果 W3 说不应该比不应该。你用什么浏览器?
  • window.matchMedia("(any-pointer: fine)").matches 出于某种原因在我所有的移动浏览器和桌面上返回 true。 window.matchMedia("(any-hover: hover)").matches 也总是返回 true,即使在没有鼠标的移动设备上也是如此。只有 window.matchMedia("(any-pointer: coarse)").matches 在移动设备上返回 true,在桌面设备上返回 false,但它不考虑连接的鼠标或 s-pen。
【解决方案3】:

如何监听文档上的 mousemove 事件。然后,在您听到该事件之前,您会假定设备只是触摸或键盘。

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

(其中listenunlisten 酌情委托给addEventListenerattachEvent。)

希望这不会导致太多的视觉卡顿,如果您需要基于模式的大量重新布局,那就太糟糕了...

【讨论】:

  • 这是一个好主意,但不幸的是,当应用程序的 UI 取决于鼠标是否可用时,响应延迟将使其无法使用。如果应用程序可能是 iframed,则尤其如此,所以只有当鼠标移到 iframe 上时,鼠标事件才会点击它。
  • 如果应用程序以启动画面和“继续”按钮启动,这可能会起作用。如果鼠标在第一个 mousedown 事件之前移动,那么你有一个鼠标。只有当按钮直接加载在鼠标下方并且用户的手非常稳定时(即使移动 1 个像素也应该拾取),它才会失败。
  • 好主意,但在我们的测试中似乎不起作用。 iPad 会触发此事件。
  • @JeffAtwood 你最后做了什么?
  • iPad 肯定会在 mousedown 事件之前触发 mousemove 事件。我发现 mousedown count > 0 和 mousedown count == mousemove count 是检测没有鼠标的好方法。我无法用真正的鼠标复制它。
【解决方案4】:

@Wyatt 的回答很棒,给了我们很多思考。

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

考虑到given order in which events are processed

  1. 触摸开始
  2. 触摸移动
  3. 触摸结束
  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);
});

这对我有用

【讨论】:

  • 对我不起作用,Ipad Safari (IOS8.3) 也用这个 sn-p 检测到鼠标
  • @netzaffin。感谢您的反馈,我发现使用 mousedown 而不是 mouseover 更加一致。你能从你的 IOS 看一下这个小提琴并让我知道结果吗?干杯jsfiddle.net/bkwb0qen/15/embedded/result
  • 如果您有带鼠标的触摸屏,则只会检测最先使用的输入法。
【解决方案5】:

只能检测浏览器是否支持触摸。无法知道它是否真的连接了触摸屏或鼠标。

如果检测到触摸功能,则可以通过侦听触摸事件而不是鼠标事件来确定使用的优先级。

跨浏览器检测触摸功能:

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

然后可以用这个来检查:

if (!hasTouch()) alert('Sorry, need touch!);

或选择要收听的事件:

var eventName = hasTouch() ? 'touchend' : 'click';
someElement.addEventListener(eventName , handlerFunction, false);

或者对触摸和非触摸使用不同的方法:

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
}

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

var hasMouse = false;

window.onmousemove = function() {
    hasMouse = true;
}

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

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

有可能编写一个插件/扩展来访问这些,但通过 JavaScript 和 DOM,这种检测是有限的,因此必须编写一个特定于各种操作系统平台的插件。

总之:这种检测只能通过“好的猜测”来估计。

【讨论】:

    【解决方案6】:

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

    如果我们真的想将该信息传达给 Javascript,我们可以使用 CSS 查询根据设备类型设置特定样式,然后在 Javascript 中使用 getComputedStyle 来读取该样式,并从它。

    但是鼠标可以随时连接拔掉,用户可能想要在触摸和鼠标之间切换。所以我们可能需要检测这种变化,并提供改变界面或自动这样做。

    【讨论】:

    • 具体来说,any-pointer and any-hover 将让您调查所有适用的设备功能。很高兴看到我们将来如何解决这个问题! :)
    • window.matchMedia("(any-pointer: rough)").matches === true ?
    【解决方案7】:

    既然您打算提供一种在界面之间切换的方法,那么简单地要求用户单击链接或按钮以“输入”应用程序的正确版本是否可行?然后你可以记住他们对未来访问的偏好。这不是高科技,但它是 100% 可靠的 :-)

    【讨论】:

    • 这实际上是一个很好的建议,但它会延迟用户进入真实界面之前的时间。另外,我必须提供一种在初始选择后进行切换的方法。最终比可以简单地检测到更多的工作..
    • 询问用户显然是最好的方法——如果不是万无一失的话——并且给你一个方便的地方来发布升级通知等等。我认为您在考虑“问题”..
    【解决方案8】:

    @SamuelRossille 不幸的是,据我所知,没有任何浏览器会暴露鼠标的存在(或缺少)。

    因此,话虽如此,我们只需要尝试并使用我们现有的选项尽力而为......事件。我知道这不是您正在寻找的……同意它目前远非理想。

    我们可以尽最大努力确定用户在任何特定时刻是否使用鼠标或触摸。这是一个使用 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');
    

    您现在可以绑定/订阅 usingMouse()usingTouch() 和/或使用 body.mouse 类设置界面样式.一旦检测到鼠标光标并在 touchstart 上,界面就会来回切换。

    希望我们很快就会从浏览器供应商那里得到一些更好的选择。

    【讨论】:

      【解决方案9】:

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

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

      【讨论】:

        【解决方案10】:

        您为什么不直接检测它是否具有感知触摸和/或对鼠标移动做出反应的能力?

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

        【讨论】:

        • 因为某些浏览器(例如IE9)报告该功能存在,即使它永远不会被触发。我相信这也是“正确”的行为。
        • 好吧,它至少适用于 OS X 的 Chrome 47。报告没有 ontouchstart。
        【解决方案11】:

        Tera-WURFL 可以通过将浏览器签名与其数据库进行比较,告诉您访问您网站的设备的功能。看看吧,它是免费的!

        【讨论】:

        • 这不适用于可能有也可能没有触摸屏和鼠标的设备。例如,台式 Windows 计算机可能连接到触摸屏,但通常也有鼠标,而平板电脑也可能运行 Windows,但可能没有连接鼠标。..
        • @Jonhoo 假设桌面操作系统连接了鼠标。毕竟,他们必须支持范围广泛的软件,而这些软件的开发并未考虑到触摸屏。
        • 运行普通 Windows 8 的平板电脑怎么样?还是Linux?还是运行 Android 的笔记本电脑?
        • @Jonhoo 显然这种方法不是最优的,但是(目前)还没有可移植的方法知道这一点。如果您正在运行一台装有 Android 系统的笔记本电脑,则假设它具有触控功能。如果您运行的是 Windows8 平板电脑,则假设它具有鼠标功能(操作系统必须模拟非触控程序的鼠标)。
        • 这已经过时了,不再相关。
        【解决方案12】:

        我遇到了同样的问题,单次触摸也被注册为点击。 在阅读了投票率最高的答案后,我想出了自己的解决方案:

        var body = document.getElementsByTagName('body')[0];
        var mouseCount = 0;
        
        // start in an undefined state 
        // (i use this to blend in elements once we decide what input is used)
        var interactionMode = 'undefined';
        
        
        var registerMouse = function() {
          // count up mouseCount every time, the mousemove event is triggered
          mouseCount++;
        
          // but dont set it instantly. 
          // instead wait 20 miliseconds (seems to be a good value for multiple move actions), 
          // if another mousemove event accoures switch to mouse as interaction 
          setTimeout(function() {
            // a touch event triggers also exactly 1 mouse move event.
            // So only if mouseCount is higher than 1 we are really moving the cursor by mouse.
            if (mouseCount > 1) {
              body.removeEventListener('mousemove', registerMouse);
              body.removeEventListener('touchend', registerTouch);
        
              interactionMode = 'mouse';
              console.log('now mousing');
              listenTouch();
            }
        
            // set the counter to zero again
            mouseCount = 0;
          }, 20);
        };
        
        var registerTouch = function() {
          body.removeEventListener('mousemove', registerMouse);
          body.removeEventListener('touchend', registerTouch);
        
          interactionMode = 'touch';
          console.log('now touching');
          mouseCount = 0;
        
          listenMouse();
        };
        
        var listenMouse = function() {
          body.addEventListener("mousemove", registerMouse);
        };
        var listenTouch = function() {
          body.addEventListener("touchend", registerTouch);
        };
        
        listenMouse();
        listenTouch();
        
        // after one second without input, assume, that we are touching
        // could be adjusted to several seconds or deleted
        // without this, the interactionMode would stay 'undefined' until first mouse or touch event
        setTimeout(function() {
          if (!body.classList.contains('mousing') || body.classList.contains('touching')) {
            registerTouch();
          }
        }, 1000);
        /* fix, so that scrolling is possible */
        
        html,
        body {
          height: 110%;
        }
        Mouse or touch me

        我发现的唯一问题是,您必须能够滚动才能正确检测触摸事件。单个标签(触摸)可能会出现问题。

        【讨论】:

          【解决方案13】:

          正如其他人所指出的,明确检测他们是否有鼠标是不可靠的。这很容易改变,具体取决于设备。这绝对是您无法使用布尔值 true 或 false 可靠地完成的事情,至少在文档规模上是这样。

          触摸事件和鼠标事件是互斥的。所以这可以在一定程度上帮助采取不同的行动。问题是触摸事件更接近鼠标上/下/移动事件,并且还会触发点击事件。

          根据您的问题,您说您希望悬停进行预览。除此之外,我不知道有关您的界面的任何其他细节。我假设在没有鼠标的情况下,您希望点击进行预览,而由于悬停预览,单击会执行不同的操作。

          如果是这种情况,您可以采取一些懒惰的方法进行检测:

          onclick 事件之前总是会出现一个带有鼠标的 onmouseover 事件。所以请注意鼠标在被点击的元素之上。

          您可以使用文档范围的 onmousemove 事件来做到这一点。您可以使用event.target 记录鼠标所在的元素。然后在你的 onclick 事件中,你可以检查鼠标是否真的在被点击的元素(或元素的子元素)上。

          从那里您可以选择依赖两者的点击事件,并根据结果执行 A 或 B 操作。如果某些触摸设备不发出点击事件(相反,您将不得不依赖 ontouch* 事件),则 B 操作可能什么都不是。

          【讨论】:

            【解决方案14】:

            我认为无法识别仅触控设备(当然据我所知)。主要问题是所有鼠标和键盘事件也是由触摸设备触发的。 请参阅以下示例,对于触摸设备,两个警报都返回 true。

            function is_touch_present() {
              return ('ontouchstart' in window) || ('onmsgesturechange' in window);
            }
            
            function is_mouse_present() {
              return (('onmousedown' in window) && ('onmouseup' in window) && ('onmousemove' in window) && ('onclick' in window) && ('ondblclick' in window) && ('onmousemove' in window) && ('onmouseover' in window) && ('onmouseout' in window) && ('oncontextmenu' in window));
            }
            
            alert("Touch Present: " + is_touch_present());
            alert("Mouse Present: " + is_mouse_present());
            

            【讨论】:

            • Safari ipad 返回true'onmousedown' in window
            【解决方案15】:

            我认为最好的主意是mousemove 监听器(目前是最佳答案)。我相信这种方法需要稍微调整一下。确实,基于触摸的浏览器甚至会模拟 mousemove 事件,正如您在 this iOS discussion 中看到的那样,所以我们应该小心一点。

            基于触摸的浏览器只会在用户点击屏幕(用户的手指向下)时模拟此事件是有道理的。这意味着我们应该在 mousemove 处理程序期间添加一个测试,以查看在事件期间哪个鼠标按钮按下(如果有)。如果没有按下鼠标按钮,我们可以安全地假设存在真正的鼠标。如果按下鼠标按钮,则测试仍然没有结果。

            那么这将如何实现呢?这个question 表明,在 mousemove 期间检查哪个鼠标按钮按下的最可靠方法是实际侦听文档级别的 3 个事件:mousemove、mousedown 和 mouseup。 up 和 down 只会设置一个全局布尔标志。移动将执行测试。如果你有一个动作并且布尔值是假的,我们可以假设鼠标存在。有关确切的代码示例,请参阅问题。

            最后的评论.. 这个测试并不理想,因为它不能在加载时执行。因此,我将使用之前建议的渐进增强方法。默认显示不支持鼠标特定悬停界面的版本。如果发现鼠标,请在运行时使用 JS 启用此模式。这对用户来说应该尽可能无缝。

            为了支持更改用户配置(即鼠标已断开连接),您可以定期重新测试。虽然我相信在这种情况下最好简单地通知用户这两种模式并让用户在它们之间手动切换(很像可以随时反转的移动/桌面选择)。

            【讨论】:

            • 感谢您提供好的解决方法建议...我认为主要问题没有得到解决,我将不得不求助于其中之一
            • 不幸的是 mousemove 在点击 ipad 时被触发。仅用模拟器测试。对于 hasMouse() 我使用的是 if( !('ontouchstart' in window) ) return true; 但不适用于支持触摸的笔记本电脑。
            【解决方案16】:

            在各种 PC、Linux、iPhone、Android 手机和标签上进行了一些测试。奇怪的是没有简单的防弹解决方案。当一些有触摸但没有鼠标的应用程序仍然存在触摸和鼠标事件时,就会出现问题。由于确实希望支持仅鼠标和仅触摸实例,因此希望同时处理两者,但这会导致用户交互的重复出现。如果可以知道设备上不存在鼠标,则可以知道忽略假/插入的鼠标事件。如果遇到 MouseMove,则尝试设置标志,但某些浏览器会抛出假的 MouseMove 以及 MouseUp 和 MouseDown。尝试检查时间戳,但认为这太冒险了。底线:我发现创建假鼠标事件的浏览器总是在插入 MouseDown 之前插入一个 MouseMove。在我 99.99% 的情况下,当在具有真正鼠标的系统上运行时,会有多个连续的 MouseMove 事件——至少两个。因此,跟踪系统是否遇到两个连续的 MouseMove 事件,如果从未满足此条件,则声明不存在鼠标。这可能太简单了,但它适用于我所有的测试设置。我想我会坚持下去,直到找到更好的解决方案。 - 吉姆 W

            【讨论】:

              【解决方案17】:

              jQuery 中检测鼠标使用的简单解决方案,它解决了移动设备也触发“mousemove”事件的问题。只需添加一个 touchstart 侦听器即可移除 mousemove 侦听器,使其不会在触摸时触发。

              $('body').one('touchstart.test', function(e) {
                // Remove the mousemove listener for touch devices
                $('body').off('mousemove.test');
              }).one('mousemove.test', function(e) {
                // MOUSE!
              });
              

              当然,设备仍然可以是触摸和鼠标,但以上将保证使用的是真正的鼠标。

              【讨论】:

                【解决方案18】:

                刚刚找到一个我认为非常优雅的解决方案。

                // flag as mouse interaction by default
                var isMouse = true;
                
                // detect a touch event start and flag it
                $(window).on('touchstart', function () {
                  // if triggers touchstart, toggle flag
                  // since touch events come before mouse event / click
                  // callback of mouse event will get in callback
                  // `isMouse === false`
                  isMouse = false;
                });
                
                // now the code that you want to write
                // should also work with `mouse*` events
                $('a.someClass').on('click', function () {
                  if (isMouse) {
                    // interaction with mouse event
                    // ...
                  } else {
                    // reset for the next event detection
                    // this is crucial for devices that have both
                    // touch screen and mouse
                    isMouse = true;
                
                    // interaction with touch event
                    // ...
                  }
                });
                

                【讨论】:

                  【解决方案19】:

                  我花了几个小时来解决我的 Phonegap 应用程序的这个问题,然后我想出了这个 hack。如果触发的事件是“被动”事件,它会生成控制台警告,这意味着它不会触发任何更改,但它可以工作!我会对任何改进的想法或更好的方法感兴趣。但这有效地允许我普遍使用 $.touch()。

                  $(document).ready(function(){
                    $("#aButton").touch(function(origElement, origEvent){
                      console.log('Original target ID: ' + $(origEvent.target).attr('id'));
                    });
                  });
                  
                  $.fn.touch = function (callback) {
                      var touch = false;
                      $(this).on("click", function(e){
                          if (!touch)
                          {
                              console.log("I click!");
                              let callbackReal = callback.bind(this);
                              callbackReal(this, e);
                          }else{
                              touch = true;
                          }
                          touch = false;
                      });
                      $(this).on("touchstart", function(e){
                          if (typeof e.touches != typeof undefined)
                          {
                              e.preventDefault();
                              touch = true;
                              console.log("I touch!");
                              let callbackReal = callback.bind(this);
                              callbackReal(this, e);
                          }
                      });
                  }
                  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
                  
                  <button id="aButton">A Button</button>

                  【讨论】:

                    【解决方案20】:

                    我在这里看到的主要问题是大多数触摸设备都会触发鼠标事件以及相应的触摸事件(touchstart -> mousedown、touchmove -> mousemove 等)。 对于只有键盘的,最后对于现代的,它们具有通用浏览器,因此您甚至无法检测到 MouseEvent 类的存在。

                    在我看来,这里不那么痛苦的解决方案是在启动时显示一个菜单(对仅键盘用户使用“alt”管理),并可能将选择存储在 localStorage/cookies/serverside 或保持相同下次访客来时选择。

                    【讨论】:

                      【解决方案21】:

                      运行 sn-p(如下)来试试这个:

                      var msg = (window.matchMedia("(any-pointer: coarse)").matches ? "Touchscreen" : "Mouse");
                      

                      var msg = (window.matchMedia("(any-pointer: coarse)").matches ? "Touchscreen" : "Mouse");
                      document.getElementById('info').innerHTML = msg;
                      body {
                        background: black;
                        color: cornflowerblue;
                      }
                      
                      #info {
                        position: absolute;
                        left: 50%;
                        top: 50%;
                        transform: translate(-50%, -50%);
                        font-size: 30vmin;
                        font-family: verdana, arial, helvetica;
                      }
                      &lt;div id='info'&gt;&lt;/div&gt;

                      (这里有更多关于window.matchMedia的信息。)

                      【讨论】:

                      • 你可以同时拥有 mousetouchscreen (一些 windows 笔记本电脑有多年的屏幕触摸功能......)
                      • window.matchMedia("(any-pointer: coarse)").matches 在移动设备上返回 true,在桌面设备上返回 false,但不考虑连接的鼠标或 s-pen。
                      【解决方案22】:

                      我强烈建议不要使用这种方法。考虑一下触摸屏、桌面大小的设备,您需要解决一系列不同的问题。

                      请让您的应用在没有鼠标的情况下可用(即没有悬停预览)。

                      【讨论】:

                      • 这正是我想要做的。我正在尝试创建一个界面,该界面可以在平板电脑(无鼠标)和鼠标上以最佳方式工作,但这些界面必然非常不同。
                      • 我同意广泛的观点。您最好使用设备检测(如 DeviceAtlas)并在加载时选择提供的界面。
                      【解决方案23】:

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

                      if (element.mouseover) {
                          //Supports the mouseover event
                      }
                      

                      请确保您不要执行以下操作:

                      if (element.mouseover()) {
                          // Supports the mouseover event
                      }
                      

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

                      您可以在此处阅读更多内容: http://www.quirksmode.org/js/support.html

                      【讨论】:

                      • 我真的不知道从你的帖子中得到什么,但是 if ('onmouseover' in $('body')[0]) alert('onmouseover');在 iPhone 中也显示一条消息
                      • 这仅检查是否定义了鼠标悬停功能,几乎所有浏览器都将使用该功能。它不会检测鼠标是否真的存在。
                      猜你喜欢
                      • 1970-01-01
                      • 2012-07-06
                      • 2017-05-17
                      • 1970-01-01
                      • 2017-08-10
                      • 1970-01-01
                      • 2021-04-23
                      • 2012-05-31
                      • 1970-01-01
                      相关资源
                      最近更新 更多