Как лучше всего обнаружить устройство с сенсорным экраном с помощью JavaScript?
Я написал плагин jQuery для использования на настольных и мобильных устройствах. Я задавался вопросом, есть ли способ с JavaScript, чтобы обнаружить, если устройство имеет возможность сенсорного экрана. Я использую jquery-mobile.js для обнаружения событий сенсорного экрана, и он работает на iOS, Android и т. Д., Но я также хотел бы написать условные операторы, основанные на том, имеет ли устройство пользователя сенсорный экран.
Это возможно?
39 ответов
Обновление: пожалуйста, прочитайте ответ 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
в последнем хроме (ст. 17). 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 делают это.
Хорошую статью, в которой объясняются проблемы с обнаружением касания, см. В статье: Stu Cox: вы не можете обнаружить сенсорный экран.
С появлением функций взаимодействия с медиа вы можете просто:
if(window.matchMedia("(any-pointer: coarse)").matches) {
// touchscreen
}
Поскольку Modernizr не обнаруживает IE10 в Windows Phone 8/WinRT, простое кросс-браузерное решение:
var supportsTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints;
Вам нужно проверять только один раз, так как устройство не будет внезапно поддерживать или не поддерживать касание, поэтому просто сохраните его в переменной, чтобы вы могли использовать его несколько раз более эффективно.
Используя все комментарии выше, я собрал следующий код, который работает для моих нужд:
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.touch
функция выше с ('ontouchstart' in document.documentElement)
Также обратите внимание, что тестирование агента пользователя iemobile
предоставит вам более широкий спектр обнаруженных мобильных устройств Microsoft, чем Windows Phone
,
Мы попробовали реализацию 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
}
});
}
I have achieved it like this;
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");
}
}
var isTouchDevice =
(('ontouchstart' in window) ||
(navigator.MaxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
if(!isTouchDevice){
/* Code for touch device /*
}else{
/* Code for non touch device */
}
Повеселись!
Самая большая проблема с попыткой обнаружить касание - на гибридных устройствах, которые поддерживают как сенсорную, так и трекпад / мышь. Даже если вы в состоянии правильно определить, поддерживает ли устройство пользователя касание, вам действительно нужно определить, какое устройство ввода в данный момент использует пользователь. Здесь есть подробное описание этой проблемы и возможное решение.
В основном подход к выяснению, касался ли пользователь экрана или вместо этого использовал мышь / трекпад, заключается в регистрации 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
})();
Подробнее здесь.
Я использовал фрагменты кода выше, чтобы определить, есть ли касание, поэтому мои фреймворки 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>
Собственно, я исследовал этот вопрос и рассмотрел все ситуации. потому что это тоже большая проблема для моего проекта. Итак, я добрался до функции ниже, она работает для всех версий всех браузеров на всех устройствах:
const isTouchDevice = () => {
const prefixes = ['', '-webkit-', '-moz-', '-o-', '-ms-', ''];
const mq = query => window.matchMedia(query).matches;
if (
'ontouchstart' in window ||
(window.DocumentTouch && document instanceof DocumentTouch)
) {
return true;
}
return mq(['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join(''));
};
Подсказка: определенноisTouchDevice
просто возвращается boolean
ценности.
Я бы не использовал ширину экрана, чтобы определить, является ли устройство сенсорным. Существуют сенсорные экраны, намного большие, чем 699 пикселей, подумайте о Windows 8. Navigatior.userAgent может быть хорошим, чтобы переопределить ложные предположения.
Я бы порекомендовал проверить этот вопрос на Modernizr.
Вы хотите проверить, поддерживает ли устройство сенсорные события или является сенсорным устройством. К сожалению, это не одно и то же.
Прочтите этот пост, он дает действительно хороший фрагмент кода для того, что делать при обнаружении сенсорных устройств или что делать, если вызывается событие 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();
}
Нет, это невозможно. Превосходные ответы даны только частично, потому что любой данный метод будет давать ложные срабатывания и ложные отрицания. Даже браузер не всегда знает, присутствует ли сенсорный экран из-за API-интерфейсов операционной системы, и этот факт может измениться во время сеанса браузера, особенно с соглашениями типа KVM.
Смотрите дальнейшие детали в этой превосходной статье:
http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
Статья предлагает вам пересмотреть предположения, которые заставляют вас хотеть обнаруживать сенсорные экраны, они, вероятно, ошибочны. (Я проверил свое собственное приложение, и мои предположения были неверны!)
В статье делается вывод:
Для макетов предположим, что у всех есть сенсорный экран. Пользователи мыши могут использовать большие элементы управления пользовательским интерфейсом гораздо легче, чем сенсорные пользователи. То же касается и парящих состояний.
Для событий и взаимодействий предположим, что у любого может быть сенсорный экран. Реализуйте взаимодействия клавиатуры, мыши и касания друг с другом, чтобы никто не блокировал друг друга.
Многие из них работают, но требуют либо jQuery, либо линтеры javascript жалуются на синтаксис. Учитывая, что ваш первоначальный вопрос требует "JavaScript" (не jQuery, не Modernizr) способа решения этой проблемы, вот простая функция, которая работает каждый раз. Это также минимально, как вы можете получить.
function isTouchDevice() {
return !!window.ontouchstart;
}
console.log(isTouchDevice());
Последнее преимущество, которое я упомяну, заключается в том, что этот код не зависит от фреймворка и устройства. Наслаждайтесь!
Все поддерживаемые браузеры, кроме Firefox для настольных компьютеров, всегда TRUE, потому что Firefox для настольных компьютеров поддерживает адаптивный дизайн для разработчиков, даже если вы нажмете сенсорную кнопку или нет!
Я надеюсь, что Mozilla исправит это в следующей версии.
Я использую рабочий стол Firefox 28.
function isTouch()
{
return !!("ontouchstart" in window) || !!(navigator.msMaxTouchPoints);
}
Именно поэтому ведутся огромные споры об обнаружении сенсорных / несенсорных устройств. Количество оконных планшетов и их размер увеличивается, что создает еще одну головную боль для веб-разработчиков.
Я использовал и тестировал ответ blmstr для меню. Меню работает так: при загрузке страницы скрипт определяет, сенсорное это устройство или нет. Исходя из этого, меню будет работать при наведении курсора (без касания) или при нажатии / касании (касании).
В большинстве случаев скрипты blmstr работали нормально (особенно сценарий 2018 года). НО было еще одно устройство, которое будет распознаваться как сенсорное, когда его нет, или наоборот.
По этой причине я немного покопался и благодаря этой статье заменил несколько строк из 4-го скрипта blmstr на следующие:
function is_touch_device4() {
if ("ontouchstart" in window)
return true;
if (window.DocumentTouch && document instanceof DocumentTouch)
return true;
return window.matchMedia( "(pointer: coarse)" ).matches;
}
alert('Is touch device: '+is_touch_device4());
console.log('Is touch device: '+is_touch_device4());
Из-за блокировки у вас ограниченный запас сенсорных устройств для тестирования этого, но пока что все работает отлично.
Я хотел бы узнать, сможет ли кто-нибудь с настольным сенсорным устройством (например, планшетом) подтвердить, что скрипт работает нормально.
Теперь о поддержке указателя: грубые медиа-запросы вроде бы поддерживаются. Я сохранил строки выше, так как у меня (по какой-то причине) были проблемы с мобильным Firefox, но строки над медиа-запросом помогают.
благодаря
Похоже, что Chrome 24 теперь поддерживает сенсорные события, вероятно, для Windows 8. Поэтому код, размещенный здесь, больше не работает. Вместо того, чтобы пытаться определить, поддерживается ли касание браузером, я теперь связываю события касания и нажатия и проверяю, вызывается ли только одно:
myCustomBind = function(controlName, callback) {
$(controlName).bind('touchend click', function(e) {
e.stopPropagation();
e.preventDefault();
callback.call();
});
};
А потом называет это:
myCustomBind('#mnuRealtime', function () { ... });
Надеюсь это поможет!
JQuery v1.11.3
В предоставленных ответах содержится много полезной информации. Но недавно я потратил много времени, пытаясь связать все воедино в рабочее решение для решения двух задач:
- Определите, что используемое устройство является устройством с сенсорным экраном.
- Обнаружить, что устройство было подключено.
Помимо этого поста и Обнаружения устройств с сенсорным экраном с помощью Javascript, я нашел этот пост Патрика Лаука чрезвычайно полезным: 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" или любое другое подобное событие, связанное с касаниями. В данном случае это элемент гиперссылки "а".
Теперь вам не нужно обрабатывать обычное нажатие мыши в JavaScript, потому что вы можете использовать CSS для обработки этих событий, используя селекторы для элемента "a" гиперссылки, например:
/* unvisited link */
a:link
{
}
/* visited link */
a:visited
{
}
/* mouse over link */
a:hover
{
}
/* selected link */
a:active
{
}
Примечание: есть и другие селекторы...
Эта проблема
Из-за гибридных устройств, которые используют комбинацию сенсорного и мышиного ввода, вы должны иметь возможность динамически изменять состояние / переменную, которая контролирует, должен ли выполняться фрагмент кода, если пользователь сенсорный или нет.
Сенсорные устройства также стреляют mousemove
на кране.
Решение
- Предположим, что прикосновение ложно при загрузке.
- Подождите, пока
touchstart
событие инициируется, затем установите его в значение true. - Если сенсорный запуск сработал, добавьте обработчик перемещения мыши.
- Если время между двумя событиями перемещения мыши было менее 20 мс, предположим, что они используют мышь в качестве входных данных. Удалите событие, так как оно больше не нужно, и mousemove - дорогостоящее событие для устройств мыши.
- Как только сенсорный запуск запускается снова (пользователь вернулся к использованию сенсорного ввода), для переменной устанавливается значение true. И повторите процесс, чтобы он определился динамично. Если каким-то чудом мышиный ход запускается дважды при прикосновении до абсурда быстро (в моем тестировании это практически невозможно сделать в течение 20 мс), следующий сенсорный запуск вернет его в значение true.
Протестировано на Safari iOS и Chrome для Android.
Примечание: не уверен на 100% в событиях указателя для MS Surface и т. Д.
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);
}
Вы можете установить модернизатор и использовать простое сенсорное событие. Это очень эффективно и работает на каждом устройстве, которое я тестировал, включая поверхность 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;
}
}
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
Практический ответ, кажется, тот, который рассматривает контекст:
1) Публичный сайт (без логина)
Код пользовательского интерфейса для совместной работы с обоими вариантами.
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 за их ответы)
Я также много боролся с различными вариантами того, как определить в Javascript, отображается ли страница на устройстве с сенсорным экраном или нет. ИМО, на данный момент не существует никакой реальной возможности правильно ее определить. Браузеры либо сообщают о событиях касания на настольных компьютерах (поскольку ОС может быть готова к касанию), либо некоторые решения работают не на всех мобильных устройствах.
В конце я понял, что с самого начала придерживался неправильного подхода: если бы моя страница выглядела одинаково на сенсорных и не сенсорных устройствах, мне, возможно, не пришлось бы беспокоиться об обнаружении свойства вообще: мой сценарий был деактивировать всплывающие подсказки над кнопками на сенсорных устройствах, поскольку они приводят к двойному нажатию, когда я хотел одним нажатием активировать кнопку.
Мое решение состояло в том, чтобы реорганизовать вид так, чтобы над кнопкой не требовалась всплывающая подсказка, и в итоге мне не нужно было обнаруживать сенсорное устройство из Javascript с помощью методов, у всех из которых есть свои недостатки.
Я знаю, что прошло много лет, но я нашел связанное решение:как связать события «touchstart» и «click», но не реагировать на оба?
Эта функция jQuery прослушивает события касания и мыши. Какое бы событие ни было запущено первым, оно отключает второе. Вот как я использовал его, чтобы заставить div реагировать только на одно из двух событий:
$(".phoneBtn").on( "mousedown touchstart", function(event) {
if (event.type == "touchstart") {
pressButton(this);
$(this).off('mousedown');
//Only touchstart event is heard
} else if (event.type == "mousedown") {
$(this).off('touchstart');
pressButton(this);
//Only mousedown event is heard
}
)}