首页 文章

什么's the best way to detect a '触摸屏'设备使用JavaScript?

提问于
浏览
352

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

那可能吗?

30 回答

  • 1

    由于Modernizr在Windows Phone 8 / WinRT上没有检测到IE10,因此一个简单的跨浏览器解决方案是:

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

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

  • 0

    使用上面的所有评论,我汇总了以下代码,以满足我的需求:

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

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

    它目前在Windows Phone 7上失败,但我还没有找到该浏览器的解决方案 .

    希望有人觉得这很有用 .

  • 0

    I like this one:

    function isTouchDevice(){
        return typeof window.ontouchstart !== 'undefined';
    }
    
    alert(isTouchDevice());
    
  • 6

    我会避免使用屏幕宽度来确定设备是否是触摸设备 . 有触摸屏远大于699px,想想Windows 8. Navigatior.userAgent可能很好地覆盖虚假的postives .

    我建议在Modernizr上查看this issue .

    您是否想要测试设备是否支持触摸事件或是否为触摸设备 . 不幸的是,这不是一回事 .

  • 132

    Update: Please read blmstr's answer below before pulling a whole feature detection library into your project. Detecting actual touch support is more complex, and Modernizr only covers a basic use case.

    Modernizr是一种在任何网站上进行各种功能检测的轻巧方式 .

    它只是为每个功能的html元素添加类 .

    然后,您可以在CSS和JS中轻松地定位这些功能 . 例如:

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

    和Javascript(jQuery示例):

    $('html.touch #popup').hide();
    
  • 17

    你尝试过使用这个功能吗? (这与Modernizr过去常用的相同 . )

    function is_touch_device() {  
      try {  
        document.createEvent("TouchEvent");  
        return true;  
      } catch (e) {  
        return false;  
      }  
    }
    

    UPDATE 1

    document.createEvent("TouchEvent") 已经开始在最新的chrome(第17节)中返回 true . Modernizr不久前更新了这个 . Check Modernizr test out here .

    像这样更新你的功能,使其工作:

    function is_touch_device() {
      return 'ontouchstart' in window;
    }
    

    UPDATE 2

    我发现以上不适用于IE10(在MS Surface上返回false) . 这是修复:

    function is_touch_device() {
      return 'ontouchstart' in window        // works on most browsers 
          || 'onmsgesturechange' in window;  // works on IE10 with some false positives
    };
    

    UPDATE 3

    'onmsgesturechange' in window 将在某些IE桌面版本中返回true,因此不可靠 . 这可以稍微更可靠地工作:

    function is_touch_device() {
      return 'ontouchstart' in window        // works on most browsers 
          || navigator.maxTouchPoints;       // works on IE10/11 and Surface
    };
    

    UPDATE 2018

    时间流逝,有新的更好的方法来测试这个 . 我基本上提取并简化了Modernizr的检查方式:

    function is_touch_device() {
      var prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');
      var mq = function(query) {
        return window.matchMedia(query).matches;
      }
    
      if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
        return true;
      }
    
      // include the 'heartz' as a way to have a non matching MQ to help terminate the join
      // https://git.io/vznFH
      var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');
      return mq(query);
    }
    

    在这里,他们使用的是非标准的 touch-enabled 媒体查询功能,我认为这有点奇怪和不好的做法 . 但是,嘿,在现实世界中,我猜它有效 . 在将来(当所有人都支持它们时),这些媒体查询功能可以为您提供相同的结果: pointerhover .

    看看source of how Modernizr are doing it .

    有关解释触摸检测问题的好文章,请参阅:Stu Cox: You Can't Detect a Touchscreen .

  • 34

    如果您使用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移动设备 .

    Also see this SO question

  • 1

    我们尝试了modernizr实现,但检测到触摸事件不再一致(IE 10在Windows桌面上有触摸事件,IE 11可以工作,因为已经删除了触摸事件并添加了指针api) .

    因此,只要我们不知道用户的输入类型,我们就决定将网站优化为触摸网站 . 这比任何其他解决方案更可靠 .

    我们的研究表明,大多数桌面用户在点击之前用鼠标移动到屏幕上,因此我们可以在他们点击或悬停任何内容之前检测它们并改变行为 .

    这是我们代码的简化版本:

    var isTouch = true;
    window.addEventListener('mousemove', function mouseMoveDetector() {
        isTouch = false;
        window.removeEventListener('mousemove', mouseMoveDetector);
    });
    
  • 2

    有什么比检查他们是否有触摸屏更好,是检查他们是否使用它,加上更容易检查 .

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

    Working Fiddle

    我这样做了;

    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
    }
    
  • 116

    这个甚至在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");
    }
    }
    
  • 1

    我使用上面的代码来检测是否触摸,因此我的fancybox iframe将显示在台式计算机上而不是触摸上 . 我注意到,当单独使用blmstr的代码时,Opera Mini for Android 4.0仍然作为非触摸设备注册 . (有谁知道为什么?)

    我最终使用:

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

    尝试检测触摸的最大"gotcha"是支持触摸和触控板/鼠标的混合设备 . 即使你're able to correctly detect whether the user' s设备支持触摸,您真正需要做的是检测用户当前使用的输入设备 . 有一个详细的写这个挑战和a possible solution here .

    基本上,确定用户是仅触摸屏幕还是使用鼠标/触控板的方法是在页面上注册 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 类,以反映此时文档上用户的 current 输入类型:

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

    更多细节here .

  • 1

    看看这个post,它提供了一个非常好的代码片段,用于检测触摸设备时的操作或调用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();
    }
    
  • 14

    不,这是不可能的 . 给出的优秀答案只是部分的,因为任何给定的方法都会产生误报和漏报 . 由于OS API,甚至浏览器也不总是知道是否存在触摸屏,并且事实可能在浏览器会话期间发生变化,特别是对于KVM类型的安排 .

    请参阅这篇优秀文章的更多细节:

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

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

    文章的结论是:

    对于布局,假设每个人都有一个触摸屏 . 鼠标用户可以比触摸用户使用小型UI控件更容易使用大型UI控件 . 悬停状态也是如此 . 对于事件和交互,假设任何人都可能有触摸屏 . 实现键盘,鼠标和触摸交互,确保彼此不会相互阻挡 .

  • 8

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

    myCustomBind = function(controlName, callback) {
    
      $(controlName).bind('touchend click', function(e) {
        e.stopPropagation();
        e.preventDefault();
    
        callback.call();
      });
    };
    

    然后叫它:

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

    希望这可以帮助 !

  • 0

    interaction media features 推出以来,你可以做到:

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

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

  • 1
    var isTouchScreen = 'createTouch' in document;
    

    要么

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

    我想是一个更彻底的检查 .

  • 1

    我用:

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

    在jQuery mobile 1.0.1中

  • 3

    所有浏览器都支持除Firefox for desktop总是 TRUE 因为Firefox支持桌面支持响应式设计,即使你点击了Touch-Button也没有!

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

    我正在使用Firefox 28桌面 .

    function isTouch()
    {
        return !!("ontouchstart" in window) || !!(navigator.msMaxTouchPoints);
    }
    
  • 1

    关于如何在Javascript中检测页面是否显示在触摸屏设备上,我也遇到了很多不同的选择 . IMO,截至目前,没有真正的选择来正确检测选项 . 浏览器要么在桌面计算机上报告触摸事件(因为操作系统可能是触摸就绪),要么某些解决方案不适用于所有移动设备 .

    最后,我意识到我从一开始就遵循了错误的方法:如果我的页面在触摸和非触摸设备上看起来类似,我可能不必担心检测属性:我的情况是通过触摸设备上的按钮停用工具提示,因为它们会导致双击,我只需要点击一下即可激活按钮 .

    我的解决方案是 refactor 视图,因此按钮上不需要工具提示,最后我不需要使用 all have their drawbacks 方法从Javascript中检测触摸设备 .

  • 1

    您可以安装现代化器并使用简单的触摸事件 . 这是非常有效的,适用于我测试过的所有设备,包括Windows表面!

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

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

    1) Public site (无登录)
    对UI进行编码以同时使用这两个选项 .

    2) Login site
    捕获登录表单上是否发生鼠标移动,并将其保存为隐藏状态输入 . 该值与登录凭据一起传递并添加到用户的 session ,因此可以在会话期间使用 .

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

    (1对@Dave Burt和1对@Martin Lantzsch的回答)

  • 2

    jQuery v1.11.3

    提供的答案中有很多好的信息 . 但是,最近我花了很多时间尝试将所有内容组合成一个有效的解决方案,以完成两件事:

    • 检测到使用的设备是否为触摸屏类型设备 .

    • 检测到设备已被点击 .

    除了这篇文章和Detecting touch screen devices with 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;
        }
    });
    

    Important! *.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 
    {
    
    }
    

    注意:还有其他选择器......

  • 3

    问题

    由于混合设备使用触摸和鼠标输入的组合,您需要能够动态更改状态/变量,该状态/变量控制在用户是否为触摸用户时是否应运行一段代码 .

    触摸设备也会点击 mousemove .

    解决方案

    • 假设加载时触摸为false .

    • 等到 touchstart 事件被触发,然后将其设置为true .

    • 如果触发了touchstart,请添加一个mousemove处理程序 .

    • 如果两次mousemove事件触发之间的时间小于20ms,则假设他们使用鼠标作为输入 . 删除不再需要的事件,mousemove对于鼠标设备来说是一个昂贵的事件 .

    • 一旦再次触发touchstart(用户返回使用触摸),该变量将重新设置为true . 并重复这个过程,因此它几乎不可能在20ms内完成它,下一个触摸开始将它重新设置为真 .

    在Safari iOS和Chrome for Android上测试过 .

    注意:对于MS Surface等的指针事件,不是100%肯定 .

    Codepen demo


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

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

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

    我要提到的最后一个好处是这个代码是框架和设备无关的 . 请享用!

  • 4

    范围jQuery support 对象:

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

    现在你可以在任何地方查看它,如下所示:

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

    到目前为止,这似乎对我来说还不错:

    //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)
    }
    
  • 20

    快速注意那些可能想写的人 window.ontouchstart !== undefined - >请使用micnic答案,因为 undefined 不是JavaScript中的关键字 . 如果开发写了类似的东西:

    undefined = window.ontouchstart;
    

    开发人员可以根据需要制作任何未定义的内容,您也可以查看 window.ontouchstart = myNonExistingWord;

    对那些给出这个答案的人不要不尊重:我很确定我也在我的代码中写了这个;)

  • 1

    当连接鼠标时,可以假设用户具有相当高的命中率(我会说实际上是100%),用户在页面准备好之后将鼠标移动至少一小段距离 - 没有任何点击 . 下面的机制检测到这一点 . 如果检测到,我认为这是缺少触摸支持的标志,或者,如果支持,在使用鼠标时不太重要 . 如果未检测到,则假定触摸设备 .

    EDIT 此方法可能不适合所有目的 . 它可用于控制基于加载页面上的用户交互而激活的功能,例如图像查看器 . 下面的代码也会将mousemove事件绑定在没有鼠标的设备上,因为它现在很突出 . 其他方法可能更好 .

    粗略地说,它是这样的(抱歉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;
    }));
    

相关问题