eventos - ¿Cuál es la mejor manera de detectar un dispositivo de ''pantalla táctil'' utilizando JavaScript?
touchstart javascript (30)
He escrito un complemento de jQuery que se utiliza tanto en dispositivos de escritorio como móviles. Me pregunté si hay una manera con JavaScript para detectar si el dispositivo tiene capacidad de pantalla táctil. Estoy usando jquery-mobile.js para detectar eventos de pantalla táctil y funciona en iOS, Android, etc., pero también me gustaría escribir sentencias condicionales en función de si el dispositivo del usuario tiene una pantalla táctil.
¿Es eso posible?
El problema
Debido a los dispositivos híbridos que utilizan una combinación de entrada táctil y de mouse, debe poder cambiar dinámicamente el estado / variable que controla si un fragmento de código debe ejecutarse si el usuario es un usuario táctil o no.
Los dispositivos táctiles también disparan el mousemove
al tocar.
Solución
- Supongamos que el toque es falso en la carga.
- Espere hasta que se
touchstart
un evento detouchstart
, luegotouchstart
en verdadero. - Si se disparó touchstart, agregue un controlador mousemove.
- Si el tiempo entre la activación de dos eventos mousemove fue inferior a 20 ms, suponga que están utilizando un mouse como entrada. Elimine el evento ya que ya no es necesario y mousemove es un evento costoso para los dispositivos de mouse.
- Tan pronto como se vuelve a disparar touchstart (el usuario volvió a usar touch), la variable vuelve a ser verdadera. Y repita el proceso para que se determine de forma dinámica. Si, por algún milagro, mousemove se dispara dos veces al toque de forma absurda y rápida (en mis pruebas es prácticamente imposible hacerlo en 20 ms), el siguiente inicio de arranque lo volverá a establecer en verdadero.
Probado en Safari iOS y Chrome para Android.
Nota: no es 100% seguro en los eventos de puntero para MS Surface, etc.
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);
}
¿Has probado con esta función? (Esto es lo mismo que solía usar Modernizr).
function is_touch_device() {
try {
document.createEvent("TouchEvent");
return true;
} catch (e) {
return false;
}
}
ACTUALIZACIÓN 1
document.createEvent("TouchEvent")
ha comenzado a document.createEvent("TouchEvent")
true
en el último chrome (v. 17). Modernizr actualizó esto hace un tiempo. Compruebe la prueba de Modernizr aquí .
Actualice su función de esta manera para que funcione:
function is_touch_device() {
return ''ontouchstart'' in window;
}
ACTUALIZACIÓN 2
Descubrí que lo anterior no estaba funcionando en IE10 (devolviendo falso en MS Surface). Aquí está la solución:
function is_touch_device() {
return ''ontouchstart'' in window // works on most browsers
|| ''onmsgesturechange'' in window; // works on IE10 with some false positives
};
ACTUALIZACIÓN 3
''onmsgesturechange'' in window
se volverá verdadero en algunas versiones de escritorio de IE así que eso no es confiable Esto funciona un poco más fiable:
function is_touch_device() {
return ''ontouchstart'' in window // works on most browsers
|| navigator.maxTouchPoints; // works on IE10/11 and Surface
};
Actualización 2018
El tiempo pasa y hay nuevas y mejores formas de probar esto. Básicamente he extraído y simplificado la forma en que Modernizr lo verifica:
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);
}
Aquí están usando la función no estándar touch-enabled
consulta de medios touch-enabled
, lo que creo que es algo extraño y una mala práctica. Pero bueno, en el mundo real supongo que funciona. En el futuro (cuando sean compatibles con todas) esas características de consulta de medios podrían proporcionarle los mismos resultados: pointer
y hover
.
Echa un vistazo a la fuente de cómo lo está haciendo Modernizr .
Para ver un buen artículo que explica los problemas con la detección táctil, consulte: Stu Cox: No puede detectar una pantalla táctil .
Como Modernizr no detecta IE10 en Windows Phone 8 / WinRT, una solución simple para todos los navegadores es:
var supportsTouch = ''ontouchstart'' in window || navigator.msMaxTouchPoints;
Solo necesita verificar una vez, ya que el dispositivo no soporta repentinamente o no soporta la función táctil, así que guárdelo en una variable para poder usarlo de manera más eficiente.
Cuando se adjunta un mouse, se puede suponer con un hitrate bastante alto (yo diría que prácticamente al 100%) que el usuario mueve el mouse al menos una pequeña distancia después de que la página esté lista, sin hacer clic. El siguiente mecanismo lo detecta. Si se detecta, considero que esto es un signo de falta de compatibilidad táctil o, si se admite, de poca importancia cuando se usa un mouse. Se supone dispositivo táctil si no se detecta.
EDITAR Este enfoque puede no encajar todos los propósitos. Se puede usar para controlar la funcionalidad que se activa según la interacción del usuario en la página cargada, por ejemplo, un visor de imágenes. El siguiente código también dejará el evento mousemove enlazado en dispositivos sin mouse, ya que se destaca ahora. Otros enfoques pueden ser mejores.
A grandes rasgos, esto es así (perdón por jQuery, pero similar en Javascript puro):
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;
}));
Echa un vistazo a esta post , que ofrece un fragmento de código realmente agradable para saber qué hacer cuando se detectan dispositivos táctiles o qué hacer si se llama al evento 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();
}
El mayor "gotcha" con el intento de detectar el contacto es en dispositivos híbridos que admiten tanto el toque como el trackpad / mouse. Incluso si puede detectar correctamente si el dispositivo del usuario es compatible con el tacto, lo que realmente necesita hacer es detectar qué dispositivo de entrada está utilizando actualmente el usuario. Hay una descripción detallada de este desafío y una posible solución aquí .
Básicamente, el método para averiguar si un usuario acaba de tocar la pantalla o si usa un mouse / trackpad en su lugar es registrar un evento de touchstart
y mouseover
en la página:
document.addEventListener(''touchstart'', functionref, false) // on user tap, "touchstart" fires first
document.addEventListener(''mouseover'', functionref, false) // followed by mouse event, ie: "mouseover"
Una acción táctil activará ambos eventos, aunque el primero ( touchstart
) siempre touchstart
primero en la mayoría de los dispositivos. Por lo tanto, contando con esta secuencia predecible de eventos, puede crear un mecanismo que agregue o elimine dinámicamente una clase de can-touch
físico a la raíz del documento para reflejar el tipo de entrada actual del usuario en este momento en el documento:
;(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
})();
Más detalles aquí .
Este funciona bien incluso en las tabletas de 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");
}
}
Esto parece estar funcionando bien para mí hasta ahora:
//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)
}
Evitaría usar el ancho de pantalla para determinar si un dispositivo es un dispositivo táctil. Hay pantallas táctiles mucho más grandes que 699px, piense en Windows 8. Navigatior.userAgent puede ser bueno para anular postivas falsas.
Recomendaría revisar este tema en Modernizr.
¿Desea probar si el dispositivo admite eventos táctiles o si es un dispositivo táctil? Desafortunadamente, eso no es lo mismo.
Hay algo mejor que verificar si tienen una pantalla táctil, es verificar si la están usando y además es más fácil de verificar.
if (window.addEventListener) {
var once = false;
window.addEventListener(''touchstart'', function(){
if (!once) {
once = true;
// Do what you need for touch-screens only
}
});
}
Intentamos la implementación modernizr, pero la detección de los eventos táctiles ya no es consistente (IE 10 tiene eventos táctiles en el escritorio de Windows, IE 11 funciona, porque se han eliminado los eventos táctiles y se ha agregado la api del puntero).
Así que decidimos optimizar el sitio web como un sitio táctil siempre y cuando no sepamos qué tipo de entrada tiene el usuario. Esto es más confiable que cualquier otra solución.
Nuestras investigaciones dicen que la mayoría de los usuarios de escritorio se mueven con el mouse sobre la pantalla antes de hacer clic, para que podamos detectarlos y cambiar el comportamiento antes de que puedan hacer clic o desplazarse sobre cualquier cosa.
Esta es una versión simplificada de nuestro código:
var isTouch = true;
window.addEventListener(''mousemove'', function mouseMoveDetector() {
isTouch = false;
window.removeEventListener(''mousemove'', mouseMoveDetector);
});
La respuesta práctica parece ser una que considera el contexto:
1) Sitio público (sin inicio de sesión)
Codifique la interfaz de usuario para trabajar con ambas opciones juntas.
2) sitio de acceso
Capture si se produjo un movimiento del mouse en el formulario de inicio de sesión y guárdelo en una entrada oculta. El valor se pasa con las credenciales de inicio de sesión y se agrega a la sesión del usuario, por lo que se puede utilizar durante la sesión.
Jquery para agregar a la página de inicio de sesión solamente
$(''#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 a @Dave Burt y +1 a @Martin Lantzsch en sus respuestas)
No, no es posible. Las excelentes respuestas que se dan son solo parciales, porque cualquier método dado producirá falsos positivos y falsos negativos. Incluso el navegador no siempre sabe si hay una pantalla táctil, debido a las API del sistema operativo, y el hecho puede cambiar durante una sesión del navegador, particularmente con arreglos de tipo KVM.
Ver más detalles en este excelente artículo:
http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
El artículo sugiere que reconsidere las suposiciones que hacen que desee detectar pantallas táctiles, probablemente estén equivocadas. (Revisé mi propia aplicación, ¡y mis suposiciones estaban realmente equivocadas!)
El artículo concluye:
Para diseños, supongamos que todos tienen una pantalla táctil. Los usuarios de mouse pueden usar controles de IU grandes mucho más fácilmente que los usuarios táctiles pueden usar los pequeños. Lo mismo ocurre con los estados flotantes.
Para eventos e interacciones, asuma que cualquiera puede tener una pantalla táctil. Implemente las interacciones del teclado, el mouse y el tacto entre sí, asegurándose de que ninguno se bloquee entre sí.
Nota rápida para aquellos que pueden sentirse tentados a escribir window.ontouchstart !== undefined
-> use micnic answer, ya que undefined
no es una palabra clave en JavaScript. Gran problema si el desarrollo escribe algo como:
undefined = window.ontouchstart;
El desarrollador puede hacer lo que desee sin definir, y también puedes verificar si window.ontouchstart = myNonExistingWord;
Sin faltar al respeto a quienes dieron esta respuesta: bastante seguro de que también escribí eso en mi código;)
Objeto de support
jQuery extensión:
jQuery.support.touch = ''ontouchend'' in document;
Y ahora puedes comprobarlo en cualquier lugar, así:
if( jQuery.support.touch )
// do touch stuff
Parece que Chrome 24 ahora admite eventos táctiles, probablemente para Windows 8. Por lo tanto, el código publicado aquí ya no funciona. En lugar de intentar detectar si el navegador admite la función táctil, ahora estoy enlazando los eventos de toque y clic y asegurándome de que solo se llame a una:
myCustomBind = function(controlName, callback) {
$(controlName).bind(''touchend click'', function(e) {
e.stopPropagation();
e.preventDefault();
callback.call();
});
};
Y luego llamándolo:
myCustomBind(''#mnuRealtime'', function () { ... });
Espero que esto ayude !
Puede utilizar el siguiente código:
function isTouchDevice() {
var el = document.createElement(''div'');
el.setAttribute(''ongesturestart'', ''return;''); // or try "ontouchstart"
return typeof el.ongesturestart === "function";
}
Fuente: Detección de la navegación basada en el tacto y @mplungjan post .
La solución anterior se basó en la detección de soporte de eventos sin el artículo de rastreo del navegador .
Puedes consultar los resultados en la siguiente página de prueba .
Tenga en cuenta que el código anterior solo prueba si el navegador es compatible con el tacto, no el dispositivo. Entonces, si tiene una computadora portátil con pantalla táctil, es posible que su navegador no tenga soporte para los eventos táctiles. El Chrome reciente admite eventos táctiles , pero otros navegadores no pueden.
También puedes probar:
if (document.documentElement.ontouchmove) {
// ...
}
pero puede que no funcione en dispositivos iPhone.
Puedes instalar el modernizador y usar un simple evento táctil. ¡Esto es muy efectivo y funciona en todos los dispositivos en los que lo he probado, incluida la superficie de las ventanas!
He creado un 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;
}
}
Si usa Modernizr , es muy fácil usar Modernizr.touch
como se mencionó anteriormente.
Sin embargo, prefiero usar una combinación de Modernizr.touch
y pruebas de agente de usuario, solo para estar seguro.
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
}
Si no usa Modernizr, simplemente puede reemplazar la función Modernizr.touch
anterior con (''ontouchstart'' in document.documentElement)
También tenga en cuenta que probar el agente de usuario iemobile
le iemobile
una gama más amplia de dispositivos móviles de Microsoft detectados que Windows Phone
.
También luché mucho con diferentes opciones sobre cómo detectar en Javascript si la página se muestra en un dispositivo de pantalla táctil o no. OMI, a partir de ahora, no existe una opción real para detectar la opción correctamente. Los navegadores informan sobre eventos táctiles en máquinas de escritorio (debido a que el sistema operativo puede estar listo para tocar), o algunas soluciones no funcionan en todos los dispositivos móviles.
Al final, me di cuenta de que estaba siguiendo el enfoque equivocado desde el principio: si mi página tuviera un aspecto similar en dispositivos táctiles y no táctiles, tal vez no debería preocuparme por detectar la propiedad: mi situación era para desactivar la información sobre herramientas en los botones de los dispositivos táctiles, ya que conducen a toques dobles en los que quería un solo toque para activar el botón.
Mi solución fue refactorizar la vista para que no se necesitara información sobre herramientas sobre un botón, y al final no tuve que detectar el dispositivo táctil de Javascript con métodos que tienen sus inconvenientes .
Todos los navegadores compatibles, excepto Firefox para escritorio, siempre son VERDADEROS, ya que Firefox para escritorio es compatible con el diseño sensible para el desarrollador, incluso si hace clic en el botón táctil o no
Espero que Mozilla arregle esto en la próxima versión.
Estoy usando el escritorio de Firefox 28.
function isTouch()
{
return !!("ontouchstart" in window) || !!(navigator.msMaxTouchPoints);
}
Usando todos los comentarios anteriores, he reunido el siguiente código que funciona para mis necesidades:
var isTouch = ((''ontouchstart'' in window) || (navigator.msMaxTouchPoints > 0));
He probado esto en iPad, Android (navegador y Chrome), Blackberry Playbook, iPhone 4s, Windows Phone 8, IE 10, IE 8, IE 10 (Windows 8 con pantalla táctil), Opera, Chrome y Firefox.
Actualmente falla en Windows Phone 7 y aún no he podido encontrar una solución para ese navegador.
Espero que alguien encuentre esto útil.
Utilicé partes del código anterior para detectar si se tocan, por lo que mis iframes de fancybox se mostrarían en las computadoras de escritorio y no en las táctiles. Noté que Opera Mini para Android 4.0 todavía se estaba registrando como un dispositivo no táctil cuando usaba el código de blmstr solo. (¿Alguien sabe por qué?)
Terminé usando:
<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>
Yo suelo:
if(jQuery.support.touch){
alert(''Touch enabled'');
}
en jQuery mobile 1.0.1
jQuery v1.11.3
Hay mucha información buena en las respuestas proporcionadas. Pero, recientemente, pasé mucho tiempo tratando de unir todo en una solución de trabajo para lograr dos cosas:
- Detectar que el dispositivo en uso es un dispositivo de tipo de pantalla táctil.
- Detecta que el dispositivo fue intervenido.
Además de esta publicación y la detección de dispositivos de pantalla táctil con Javascript , encontré esta publicación de Patrick Lauke extremadamente útil: https://hacks.mozilla.org/2013/04/detecting-touch-its-the-why-not-the-how/
Aquí está el código ...
$(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;
}
});
¡Importante! El *.on( events [, selector ] [, data ], handler )
necesita tener un selector, generalmente un elemento, que pueda manejar el evento "touchstart", o cualquier otro evento similar asociado con toques. En este caso, es el elemento de hipervínculo "a".
Ahora, no es necesario manejar el clic normal del mouse en JavaScript, ya que puede usar CSS para manejar estos eventos usando selectores para el hipervínculo "a" elemento, así:
/* unvisited link */
a:link
{
}
/* visited link */
a:visited
{
}
/* mouse over link */
a:hover
{
}
/* selected link */
a:active
{
}
Nota: También hay otros selectores ...
Lo he conseguido así;
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
}
Actualización: lea la respuesta de blmstr a continuación antes de incluir una biblioteca completa de detección de características en su proyecto. Detectar el soporte táctil real es más complejo, y Modernizr solo cubre un caso de uso básico.
Modernizr es una manera excelente y liviana de hacer todo tipo de detección de características en cualquier sitio.
Simplemente agrega clases al elemento html para cada característica.
A continuación, puede orientar esas características fácilmente en CSS y JS. Por ejemplo:
html.touch div {
width: 480px;
}
html.no-touch div {
width: auto;
}
Y Javascript (ejemplo de jQuery):
$(''html.touch #popup'').hide();
Me gusta este:
function isTouchDevice(){
return typeof window.ontouchstart !== ''undefined'';
}
alert(isTouchDevice());
$.support.touch ? "true" : "false";
var isTouchScreen = ''createTouch'' in document;
o
var isTouchScreen = ''createTouch'' in document || screen.width <= 699 ||
ua.match(/(iPhone|iPod|iPad)/) || ua.match(/BlackBerry/) ||
ua.match(/Android/);
sería un control más completo, supongo.