Определение сенсорных устройств с помощью JavaScript
Определение сенсорного ввода звучит просто, пока вы не столкнётесь с Surface Pro, iPad с Magic Keyboard или Chromebook с сенсорным экраном. Эти гибридные устройства разрушают предположение о том, что «сенсорный ввод» и «мышь» взаимоисключающи — и именно здесь большинство подходов к определению терпят неудачу.
Эта статья отсекает лишнее: что действительно работает, что нет, и когда вообще не стоит определять наличие сенсорного ввода.
Ключевые выводы
'ontouchstart' in windowненадёжен, потому что браузеры предоставляют его непоследовательно, даже на устройствах без сенсорного экрана.navigator.maxTouchPoints > 0— наиболее надёжная простая JavaScript-проверка заявленной возможности сенсорного ввода в современных браузерах.- CSS медиа-запросы для указателей (
pointer,any-pointer,hover) обрабатывают большинство адаптаций UI без какого-либо JavaScript. - Pointer Events API и его свойство
pointerTypeпозволяют определить текущий метод ввода пользователя, что гораздо полезнее на гибридных устройствах, чем однократная проверка при загрузке страницы.
Почему 'ontouchstart' in window недостаточно
Годами разработчики проверяли 'ontouchstart' in window как быстрый способ определения возможности сенсорного ввода. Проблема в том, что браузеры предоставляют это свойство непоследовательно. Некоторые десктопные браузеры на Windows 8+ возвращают true даже без сенсорного экрана. Chrome менял своё поведение в разных версиях. Это ненадёжный сигнал.
Полагаясь только на ontouchstart, вы определяете поведение браузера, а не фактические возможности оборудования.
navigator.maxTouchPoints: более надёжная базовая проверка
Наиболее надёжным JavaScript-свойством для определения поддержки сенсорного ввода сегодня является navigator.maxTouchPoints. Оно возвращает максимальное количество одновременных точек касания, которые поддерживает устройство. Значение больше нуля означает, что оборудование сообщает о возможности сенсорного ввода.
function hasTouchSupport() {
return navigator.maxTouchPoints > 0
}
Это часть спецификации Pointer Events и поддерживается всеми современными браузерами — Chrome, Firefox, Safari и Edge. Это чисто, читаемо и не зависит от проверки обработчиков событий.
Примечание:
navigator.msMaxTouchPointsбыл эквивалентом эпохи IE. Он вам не нужен, если вы не поддерживаете устаревший код.
Когда CSS медиа-запросы для указателей — лучший инструмент
Для большинства адаптаций UI вам вообще не нужен JavaScript. CSS медиа-запросы для указателей позволяют настраивать стили на основе точности основного устройства ввода.
/* Нацелен на устройства, где основной указатель грубый (например, палец) */
@media (pointer: coarse) {
.btn {
min-height: 48px;
}
}
/* Нацелен на любой доступный грубый указатель, включая вторичные устройства ввода */
@media (any-pointer: coarse) {
.tooltip {
display: none;
}
}
/* Нацелен на устройства, где наведение недоступно надёжно */
@media (hover: none) {
.dropdown:hover .menu {
display: none;
}
}
Разница между pointer и any-pointer важна на гибридных устройствах. pointer: coarse отражает только основное устройство ввода. any-pointer: coarse возвращает true, если любое подключённое устройство ввода является грубым — полезно, когда у устройства есть и мышь, и сенсорный экран.
Discover how at OpenReplay.com.
Pointer Events против Touch Events
Pointer Events — это современная унифицированная модель для обработки ввода от мыши, касания и стилуса в JavaScript. Вместо поддержки отдельных обработчиков touchstart и mousedown вы пишете один:
element.addEventListener('pointerdown', (e) => {
if (e.pointerType === 'touch') {
// Обработка сенсорного ввода
} else if (e.pointerType === 'mouse') {
// Обработка ввода мыши
}
})
Свойство pointerType точно сообщает вам, что пользователь делает прямо сейчас — а не на что теоретически способно его устройство. Это различие наиболее важно на гибридных устройствах.
Устаревшие Touch Events (touchstart, touchmove, touchend) всё ещё поддерживаются в большинстве браузеров, но доступны не во всех окружениях и не охватывают ввод мыши или стилуса. Предпочитайте Pointer Events для нового кода.
Проблема гибридных устройств
Пользователь на ноутбуке с сенсорным экраном может начать сеанс с мышью, а затем потянуться и коснуться экрана. Любое определение, которое вы выполнили при загрузке страницы, уже устарело.
Вместо того чтобы фиксировать единственный результат определения, слушайте события pointerdown и динамически читайте e.pointerType. Это позволяет адаптировать UI на основе того, что пользователь действительно делает, а не того, что теоретически поддерживает его устройство.
Что использовать и когда
| Цель | Рекомендуемый подход |
|---|---|
| Настройка размеров кнопок или областей касания | CSS @media (pointer: coarse) |
| Отключение UI только для наведения | CSS @media (hover: none) |
| Проверка возможности сенсорного оборудования в JS | navigator.maxTouchPoints > 0 |
| Обработка событий ввода всех типов | Pointer Events API |
| Динамическое определение текущего типа ввода | event.pointerType на pointerdown |
Заключение
Полностью откажитесь от определения по user agent. Для адаптаций UI CSS медиа-запросы для указателей обрабатывают большинство случаев без единой строки JavaScript. Когда вам действительно нужен JavaScript, navigator.maxTouchPoints даёт надёжный сигнал от оборудования, а Pointer Events API предоставляет контекст ввода в реальном времени. Вместе они охватывают весь спектр современных устройств — включая гибридные, которые делают простое определение ненадёжным.
Часто задаваемые вопросы
Да. navigator.maxTouchPoints является частью спецификации Pointer Events и поддерживается в Chrome, Firefox, Safari и Edge. Обычно он возвращает ноль на устройствах, которые не сообщают о поддержке сенсорного ввода, и положительное целое число на устройствах с сенсорным вводом. Это наиболее надёжная проверка по одному свойству, доступная в JavaScript сегодня для определения поддержки сенсорного ввода.
Строки user agent ненадёжны для определения возможностей ввода. Они идентифицируют браузер и операционную систему, а не оборудование. Ноутбук на Windows и планшет на Windows могут иметь одинаковую строку user agent, несмотря на совершенно разные методы ввода. Определение возможностей через maxTouchPoints или CSS медиа-запросы гораздо точнее.
Да. Pointer Events API предоставляет свойство pointerType для событий типа pointerdown. Его значение — touch, mouse или pen в зависимости от используемого в данный момент устройства ввода. Это полезнее, чем однократная проверка возможностей, особенно на гибридных устройствах, где пользователи переключаются между устройствами ввода.
Медиа-запрос pointer нацелен только на основное устройство ввода. Запрос any-pointer возвращает true, если любое доступное устройство ввода соответствует условию. На ноутбуке с тачпадом и сенсорным экраном pointer: coarse может быть false, потому что тачпад является основным, но any-pointer: coarse будет true, потому что сенсорный экран подходит под условие.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.