Каков наилучший способ обнаружения устройства с сенсорным экраном с помощью JavaScript?

Я написал плагин jQuery, который используется как на настольных, так и на мобильных устройствах. Я задавался вопросом, есть ли способ с JavaScript определить, имеет ли устройство возможность сенсорного экрана. Я использую jQuery-mobile.js для обнаружения событий сенсорного экрана и работает на iOS, Android и т. д., но я также хотел бы написать условные операторы, основанные на том, имеет ли устройство пользователя сенсорный экран.

Это возможно?

30 ответов


обновление: пожалуйста, прочитайте ответ blmstr ниже, прежде чем тянуть всю библиотеку обнаружения функций в ваш проект. Обнаружение фактической сенсорной поддержки является более сложным, и Modernizr охватывает только базовый случай использования.

Modernizr отличный, легкий способ сделать все виды распознавания компонентов на любом сайте.

Он просто добавляет классы к элементу html для каждой функции.

затем вы можете нацелить эти функции легко в CSS и JS. Например:

html.touch div {
    width: 480px;
}

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

и Javascript (пример jQuery):

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

вы пробовали использовать эту функцию? (Это то же самое, что использовал Modernizr.)

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

обновление 1

document.createEvent("TouchEvent") начали возвращаться true в последнем chrome (V. 17). Modernizr это некоторое время назад обновили. Проверьте тест Modernizr здесь.

обновите свою функцию так, чтобы она работала:

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

обновление 2

я обнаружил, что выше не было работа с IE10 (возврат false на MS Surface). Вот исправление:

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

обновление 3

'onmsgesturechange' in window вернет true в некоторых настольных версиях IE, так что это не надежно. Это работает несколько надежнее:

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

обновление 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 функция медиа-запроса, которая, по-моему, странная и плохая практика. Но в реальном мире это работает. В будущем (когда они поддерживаются всеми) эти функции медиа-запросов могут дать вам те же результаты:pointer и hover.

Проверьте источник того, как Modernizr делают это.

для хорошей статьи, которая объясняет проблемы с сенсорным обнаружением, см.: Стью Кокс: Ты Не удается обнаружить сенсорный экран.


поскольку Modernizr не обнаруживает IE10 на Windows Phone 8 / WinRT, простое кросс-браузерное решение:

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

вам нужно только один раз проверить, так как устройство не будет внезапно поддерживать или не поддерживать touch, поэтому просто сохраните его в переменной, чтобы вы могли использовать его несколько раз более эффективно.


используя все комментарии выше, я собрал следующий код, который работает для моих потребностей:

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, и я еще не смог найти решение для этого браузера.

надеюсь, кто-то найдет это полезным.


мне нравится этот:

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

alert(isTouchDevice());

если вы используете 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, вы можете просто заменить


мы попробовали реализацию modernizr, но обнаружение событий касания больше не согласовано (IE 10 имеет события касания на рабочем столе windows, IE 11 работает, потому что события касания отброшены и добавлен api указателя).

поэтому мы решили оптимизировать веб-сайт как сенсорный сайт, пока мы не знаем, какой тип ввода имеет пользователь. Это более надежное решение, чем любое другое.

наши исследования говорят, что большинство пользователей рабочего стола перемещаются с помощью мыши по экрану прежде чем они щелкнут, чтобы мы могли обнаружить их и изменить поведение, прежде чем они смогут щелкнуть или навести что-либо.

это упрощенная версия кода:

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

есть что-то лучше, чем проверить, есть ли у них сенсорный экран, - это проверить, используют ли они его, плюс это легче проверить.

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

Рабочая Скрипку

Я добился так:

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
}

Это хорошо работает даже в планшетах 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");
}
}

я использовал фрагменты кода выше, чтобы определить, касается ли touch, поэтому мои iframes fancybox будут отображаться на настольных компьютерах, а не на ощупь. Я заметил, что Opera Mini для Android 4.0 по-прежнему регистрируется как устройство без касания при использовании только кода blmstr. (Кто-нибудь знает почему?)

в итоге я использовал:

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

самый большой "gotcha" с попыткой обнаружить касание находится на гибридных устройствах, которые поддерживают как сенсорный, так и трекпад/мышь. Даже если вы можете правильно определить, поддерживает ли устройство пользователя touch, вам действительно нужно определить, какое устройство ввода пользователь в настоящее время использование. Там подробно написать об этом вызове и возможное решение здесь.

в основном подход к выяснению того, коснулся ли пользователь только что экран или используется мышь/ трекпад вместо этого, чтобы зарегистрировать оба touchstart и mouseover событие на странице:

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 класс в корень документа, чтобы отразить настоящее тип входного сигнала потребителя в этот момент на документ:

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

более подробная информация здесь.


зацените в должности, это дает действительно хороший фрагмент кода для того, что делать, когда сенсорные устройства обнаружены или что делать, если событие 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();
}

Я бы избегал использовать ширину экрана, чтобы определить, является ли устройство сенсорным устройством. Есть сенсорные экраны намного больше, чем 699px, подумайте о Windows 8. Navigatior.userAgent может быть приятно переопределить ложные postives.

Я бы рекомендовал проверить этот вопрос на Modernizr.

вы хотите проверить, поддерживает ли устройство сенсорные события или сенсорное устройство. К сожалению, это не одно и то же.


нет, это невозможно. Превосходные ответы всегда частичны, потому что любой метод порождает ложные положительные и ложные отрицательные стороны. Даже браузер не всегда знает, присутствует ли сенсорный экран, из-за API ОС, и этот факт может измениться во время сеанса браузера, особенно с договоренностями типа KVM.

см. более подробную информацию в этом превосходном статья:

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

статья предлагает вам пересмотреть предположения, которые заставляют вас обнаруживать сенсорные экраны, они, вероятно, ошибочны. (Я проверил свое собственное приложение, и мои предположения были действительно неправильными!)

статья завершается:

для макетов предположим, что у всех есть сенсорный экран. Пользователи мыши могут использовать большие элементы управления UI намного проще, чем сенсорные пользователи можно использовать маленькие те. То же самое касается состояний наведения.

для событий и взаимодействий предположим, что у кого-то может быть сенсорный экран. Реализация клавиатуры, мыши и сенсорных взаимодействий друг с другом, обеспечение не блокируют друг друга.


похоже, что Chrome 24 теперь поддерживает сенсорные события, вероятно, для Windows 8. Таким образом, код, размещенный здесь, больше не работает. Вместо того, чтобы пытаться определить, поддерживается ли touch браузером, я теперь связываю события touch и click и удостоверяюсь, что только один называется:

myCustomBind = function(controlName, callback) {

  $(controlName).bind('touchend click', function(e) {
    e.stopPropagation();
    e.preventDefault();

    callback.call();
  });
};

и затем называя его:

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

надеюсь, что это помогает !


var isTouchScreen = 'createTouch' in document;

или

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

было бы более тщательной проверкой, я полагаю.


Я использую:

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

в jQuery mobile 1.0.1


все браузеры поддерживаются, кроме Firefox для рабочего стола всегда TRUE из-за Firefox для настольной поддержки адаптивного дизайна для разработчика даже вы нажимаете сенсорную кнопку или нет!

Я надеюсь, что Mozilla исправит это в следующей версии.

Я использую рабочий стол Firefox 28.

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

Я также много боролся с различными вариантами о том, как определить в Javascript, отображается ли страница на устройстве с сенсорным экраном или нет. ИМО, на данный момент, не существует реального варианта для правильного обнаружения этого варианта. Браузеры либо сообщают о сенсорных событиях на настольных компьютерах (потому что ОС может быть touch-ready), либо некоторые решения не работают на всех мобильных устройствах.

в конце концов, я понял, что я следовал неправильному подходу с самого начала: Если бы моя страница была похожа на touch и не-сенсорные устройства, я, возможно, не должен беспокоиться об обнаружении свойства вообще: Мой сценарий состоял в том, чтобы деактивировать подсказки над кнопками на сенсорных устройствах, поскольку они приводят к двойным нажатиям, где я хотел, чтобы один кран активировал кнопку.

моим решением было рефакторинг представление так, что никакая подсказка не нужна была над кнопкой, и в конце концов мне не нужно было обнаруживать сенсорное устройство из Javascript методами, которые у всех есть свои недостатки.


вы можете установить модернизатор и использовать простое сенсорное событие. Это очень эффективно и работает на каждом устройстве, которое я тестировал, включая Windows surface!

Я создал 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;
    }
}

практический ответ, похоже, тот, который учитывает контекст:

1) общественное место (нет входа)
Код UI для работы с обоими параметрами вместе.

2) Вход на сайт
Зафиксируйте, произошло ли перемещение мыши в форме входа в систему, и сохраните его в скрытый вход. Значение передается с учетными данными для входа и добавляется к сессии, поэтому его можно использовать для продолжительности сессия.

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 на их ответы)


С jQuery версии v1.11.3

В представленных ответах содержится много хорошей информации. Но в последнее время я потратил много времени, пытаясь связать все вместе в рабочее решение для выполнения двух вещей:

  1. определите, что используемое устройство является устройством типа сенсорного экрана.
  2. обнаружить, что аппарат прослушивается.

кроме этого поста и обнаружение устройств с сенсорным экраном с помощью Javascript, I нашел этот пост Патриком Лауке чрезвычайно полезным: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;
    }
});

важно! на *.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 
{

}

Примечание: есть и другие селекторы...


проблема

из-за гибридных устройств, которые используют комбинацию сенсорного ввода и ввода мыши, вы должны быть в состоянии динамически измените состояние / переменную, которая определяет, должен ли выполняться фрагмент кода, если пользователь является пользователем touch или нет.

сенсорные устройства также огонь mousemove on tap.

решение

  1. предположим, что касание ложно при загрузке.
  2. подождите, пока touchstart событие запускается, то установите его в истинный.
  3. если touchstart был уволен, добавьте обработчик mousemove.
  4. если время между двумя событиями mousemove меньше 20 мс, предположим, что они используют мышь в качестве входных данных. Удалите событие, поскольку оно больше не требуется, и mousemove-дорогостоящее событие для устройств мыши.
  5. как только touchstart запускается снова (пользователь вернулся к использованию touch), переменная возвращается к true. И повторите процесс, чтобы он был определен динамично. Если чудо mousemove увольняется дважды на ощупь абсурдно быстро (в моем тестировании это практически невозможно сделать в течение 20 мс), следующий touchstart установит его обратно в true.

протестировано на Safari iOS и Chrome для Android.

Примечание: не на 100% уверен в указателе-события для MS Surface и т. д.

сайт CodePen демо


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

экстент jQuery


Это, кажется, работает хорошо для меня до сих пор:

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

быстрое Примечание Для тех, кто может соблазниться написать window.ontouchstart !== undefined -- > пожалуйста, используйте micnic ответ, как undefined не является ключевым словом в JavaScript. Большая проблема, если develop пишет что-то вроде:

undefined = window.ontouchstart;

разработчик может сделать неопределенное все, что они хотят, и вы также можете проверить, если window.ontouchstart = myNonExistingWord;

нет неуважения к тем, кто дал этот ответ: довольно уверен, что я написал это в своем коде тоже ;)


когда мышь прикреплена, можно предположить с довольно высокой скоростью (я бы сказал, на практике 100%), что пользователь перемещает мышь, по крайней мере, на небольшое расстояние после того, как страница готова - без щелчка. Механизм ниже обнаруживает это. При обнаружении я рассматриваю это как признак отсутствия сенсорной поддержки или, если поддерживается, незначительной важности, когда используется мышь. Touch-устройство предполагается, если не обнаружено.

редактировать этот подход может не соответствовать всем целям. Это может быть используется для управления функциями, активируемыми на основе взаимодействия с пользователем на загруженной странице, например, вьюером изображений. Приведенный ниже код также оставит событие 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;
}));

Вы можете использовать следующий код:

function isTouchDevice() {
   var el = document.createElement('div');
   el.setAttribute('ongesturestart', 'return;'); // or try "ontouchstart"
   return typeof el.ongesturestart === "function";
}

источник: обнаружение сенсорного просмотра и @mplungjan post.

вышеуказанное решение было основано на обнаружение поддержки событий без обнюхивания браузера статьи.

Вы можете проверить результаты по следующим тестовая страница.

обратите внимание, что приведенный выше код проверяет только, поддерживает ли браузер touch, а не устройство. Так что если у вас есть ноутбук с сенсорным экраном, Ваш браузер может не иметь поддержки сенсорных событий. The последние Chrome поддерживает сенсорные события, но другой браузер не может.

вы также можете попробовать:

if (document.documentElement.ontouchmove) {
  // ...
}

но он может не работать на устройствах iPhone.


$.support.touch ? "true" : "false";