Cómo eliminar / ignorar: desplazar el estilo CSS en dispositivos táctiles

141

Quiero ignorar todas las :hoverdeclaraciones CSS si un usuario visita nuestro sitio web a través de un dispositivo táctil. Debido a que el :hoverCSS no tiene sentido, e incluso puede ser molesto si una tableta lo activa al hacer clic / tocar, ya que podría quedarse hasta que el elemento pierda el foco. Para ser honesto, no sé por qué los dispositivos táctiles sienten la necesidad de activarse :hoveren primer lugar, pero esta es la realidad, por lo que este problema también es la realidad.

a:hover {
   color:blue;
   border-color:green;
   // etc. > ignore all at once for touch devices
}

Entonces, ¿cómo puedo eliminar / ignorar todas las :hoverdeclaraciones CSS a la vez (sin tener que conocer cada una) para dispositivos táctiles después de haberlos declarado?

Simon Ferndriger
fuente
Esto es similar a Cómo evitar los efectos de
desplazamiento permanente
1
Lo soluciono con una solución php donde descubro si el dispositivo de visualización es móvil o no y uso la hoja de estilo correcta con varios cambios basados ​​en eso. Sin embargo, eso no responde a tu pregunta. Potencialmente, podría usar consultas @media, pero no está garantizado que funcione cuando los teléfonos y tabletas tienen resoluciones full hd.
TomFirth
1
Posible duplicado de Desactivar efectos de
desplazamiento

Respuestas:

171

tl; dr usa esto: https://jsfiddle.net/57tmy8j3/

Si le interesa por qué o qué otras opciones hay, siga leyendo.

Quick'n'dirty - eliminar: estilos de desplazamiento con JS

Puede eliminar todas las reglas CSS que contienen el :hoveruso de Javascript. Esto tiene la ventaja de no tener que tocar CSS y ser compatible incluso con navegadores antiguos.

function hasTouch() {
  return 'ontouchstart' in document.documentElement
         || navigator.maxTouchPoints > 0
         || navigator.msMaxTouchPoints > 0;
}

if (hasTouch()) { // remove all the :hover stylesheets
  try { // prevent exception on browsers not supporting DOM styleSheets properly
    for (var si in document.styleSheets) {
      var styleSheet = document.styleSheets[si];
      if (!styleSheet.rules) continue;

      for (var ri = styleSheet.rules.length - 1; ri >= 0; ri--) {
        if (!styleSheet.rules[ri].selectorText) continue;

        if (styleSheet.rules[ri].selectorText.match(':hover')) {
          styleSheet.deleteRule(ri);
        }
      }
    }
  } catch (ex) {}
}

Limitaciones: las hojas de estilo deben estar alojadas en el mismo dominio (eso significa que no hay CDN). Deshabilita los movimientos del mouse sobre dispositivos táctiles y de mouse mixtos como Surface o iPad Pro, lo que perjudica a UX.

Solo CSS: use consultas de medios

Coloque todas sus reglas de desplazamiento en un @mediabloque:

@media (hover: hover) {
  a:hover { color: blue; }
}

o, alternativamente, anule todas sus reglas de desplazamiento (compatible con navegadores antiguos):

a:hover { color: blue; }

@media (hover: none) {
  a:hover { color: inherit; }
}

Limitaciones: funciona solo en iOS 9.0+, Chrome para Android o Android 5.0+ cuando se usa WebView. hover: hoverrompe los efectos de desplazamiento en navegadores antiguos, hover: nonenecesita anular todas las reglas CSS previamente definidas. Ambos son incompatibles con dispositivos mixtos de mouse y táctiles .

El más robusto: detecta el tacto a través de JS y antepone CSS: reglas de desplazamiento

Este método necesita anteponer todas las reglas de desplazamiento con body.hasHover. (o un nombre de clase de su elección)

body.hasHover a:hover { color: blue; }

La hasHoverclase se puede agregar usando hasTouch()el primer ejemplo:

if (!hasTouch()) document.body.className += ' hasHover'

Sin embargo, esto tendría los mismos inconvenientes con los dispositivos táctiles mixtos que los ejemplos anteriores, lo que nos lleva a la solución definitiva. Habilite los efectos de desplazamiento cada vez que mueva el cursor del mouse, desactive los efectos de desplazamiento siempre que se detecte un toque.

function watchForHover() {
  // lastTouchTime is used for ignoring emulated mousemove events
  let lastTouchTime = 0

  function enableHover() {
    if (new Date() - lastTouchTime < 500) return
    document.body.classList.add('hasHover')
  }

  function disableHover() {
    document.body.classList.remove('hasHover')
  }

  function updateLastTouchTime() {
    lastTouchTime = new Date()
  }

  document.addEventListener('touchstart', updateLastTouchTime, true)
  document.addEventListener('touchstart', disableHover, true)
  document.addEventListener('mousemove', enableHover, true)

  enableHover()
}

watchForHover()

Esto debería funcionar básicamente en cualquier navegador y habilita / deshabilita los estilos de desplazamiento según sea necesario.

Aquí está el ejemplo completo: moderno: https://jsfiddle.net/57tmy8j3/
Legacy (para usar con navegadores antiguos): https://jsfiddle.net/dkz17jc5/19/

espada
fuente
¡Gracias! Solo un comentario: ¿no sería mejor verificar primero si document.styleSheetsexiste en lugar de usar el try/catch?
Simon Ferndriger
1
Lo he usado try/catchen caso de que aparezca algún error extraño, que podría bloquear el script, como deleteRule no funciona debido a la política del mismo origen o el soporte escaso en IE (editó el comentario para reflejar eso). Pero sí, en la mayoría de los casos, solo una simple verificación debería ser suficiente.
cuchilla
1
Vale la pena mencionar que los navegadores de escritorio, incluido Firefox, no generarán 'notouch'. Informan que comprenden los eventos táctiles.
Harry B
3
Esta solución no funciona para dispositivos mixtos donde el desplazamiento debe funcionar para el mouse pero no para eventos táctiles
nbar
2
@Haneev es correcto para la mayoría de los casos, aunque no para el específico. Hacer que su regla funcione agregaría mucha complejidad al código, lo que dificultaría la comprensión de la respuesta. Sugeriría dividir las reglas o usar la segunda solución, que de todos modos es más robusta. Sin embargo, puede sugerir una edición, si puede mejorar la primera solución sin hacerla demasiado complicada.
cuchilla
43

Adaptación de puntero al rescate!

Como esto no se ha tocado en un tiempo, puede usar:

a:link {
   color: red;
}

a:hover {
   color:blue;
}

@media (hover: none) {
   a:link {
      color: red;
   }
}

Vea esta demostración tanto en su navegador de escritorio como en el navegador de su teléfono . Compatible con dispositivos táctiles modernos .

Nota : Tenga en cuenta que, dado que la entrada (capacidad) primaria de una PC de Surface es un mouse, terminará siendo un enlace azul, incluso si se trata de una pantalla separada (tableta). Los navegadores siempre (deberían) predeterminan la capacidad de entrada más precisa.

Jason T Featheringham
fuente
¡Gracias, esto sería conveniente! Sin embargo, lo probé con Chrome en modo móvil, pero no funcionó: cuando lo "toqué", se puso azul de todos modos ...
Simon Ferndriger
1
@SimonFerndriger Eso es solo una limitación de las herramientas de Chrome. Intente abrir el enlace en un dispositivo táctil real.
Jason T Featheringham
1
Funciona perfectamente en iPad Chrome / Safari vs. OS X Chrome / Safari. ¡Gracias! Finalmente, la solución elegante que desearías para un problema visual tan pequeño.
rakaloof
3
Acabo de probar esto en mi iPhone 6S real con iOS 11, y el enlace se volvió azul.
Lukas Petr
1
También probé esto con Nexus 5X con Android 8.1.0 y Chrome 68.0.3440.91. El enlace se volvió azul.
Trevin Avery
12

He encontrado el mismo problema (en mi caso con los navegadores móviles Samsung) y, por lo tanto, me topé con esta pregunta.

Gracias a la respuesta de Calsal, encontré algo que creo que excluirá prácticamente todos los navegadores de escritorio porque parece ser reconocido por los navegadores móviles que probé (vea la captura de pantalla de una tabla compilada: tabla de detección de funciones de puntero CSS ).

Los documentos web de MDN indican que

La función puntero CSS @media se puede usar para aplicar estilos en función de si el mecanismo de entrada principal del usuario es un dispositivo señalador y, de ser así, qué tan preciso es

.

Lo que descubrí es que puntero: grueso es algo que todos los navegadores de escritorio de la tabla adjunta desconocen pero que todos los navegadores móviles de la misma tabla conocen. Esta parece ser la opción más efectiva porque todos los demás valores de palabras clave de puntero dan resultados inconsistentes.

Por lo tanto, podría construir una consulta de medios como Calsal, descrita pero ligeramente modificada. Utiliza una lógica inversa para descartar todos los dispositivos táctiles.

Sass mixin:

@mixin hover-supported {    
    /* 
     * https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer 
     * coarse: The primary input mechanism includes a pointing device of limited accuracy.
     */
    @media not all and (pointer: coarse) {
        &:hover {
            @content;
        }
    }
}

a {
    color:green;
    border-color:blue;

    @include hover-supported() {
        color:blue;
        border-color:green;
    }
}

CSS compilado:

a {
  color: green;
  border-color: blue;
}
@media not all and (pointer: coarse) {
  a:hover {
    color: blue;
    border-color: green;
  }
}

También se describe en esta esencia que creé después de investigar el problema. Codepen para la investigación empírica.

ACTUALIZACIÓN : Al escribir esta actualización, 2018-08-23, y señalado por @DmitriPavlutin, esta técnica ya no parece funcionar con el escritorio de Firefox.

3 revoluciones
fuente
Gracias @DmitriPavlutin, de acuerdo con la tabla (cuando se compiló) debería, pero podría echarle otro vistazo.
Programador Por
@DmitriPavlutin De hecho, tienes razón. Actualizará la respuesta
ProgrammerPer
1
¿Quiere decir que no funciona en Firefox Desktop si se usa con un dispositivo exclusivamente táctil? Funciona muy bien con todo lo que he probado.
FuzzeeLowgeek
@FuzzeeLowgeek ¡Suena bien! La última vez que revisé fue el 23-08-2018, así que si puedes confirmar esto con seguridad, ¿tal vez pueda actualizar la respuesta nuevamente?
Programador Por
9

De acuerdo con la respuesta de Jason , podemos abordar solo los dispositivos que no admiten el desplazamiento con consultas puras de medios css. También podemos abordar solo dispositivos que admiten el desplazamiento , como la respuesta de moogal en una pregunta similar, con @media not all and (hover: none). Se ve raro pero funciona.

Hice un Sass mixin con esto para un uso más fácil:

@mixin hover-supported {
    @media not all and (hover: none) {
        &:hover {
            @content;
        }
    }
}

Actualización 2019-05-15 : recomiendo este artículo de Medium que pasa por todos los dispositivos diferentes a los que podemos dirigirnos con CSS. Básicamente es una combinación de estas reglas de medios, combínelas para objetivos específicos:

@media (hover: hover) {
    /* Device that can hover (desktops) */
}
@media (hover: none) {
    /* Device that can not hover with ease */
}
@media (pointer: coarse) {
    /* Device with limited pointing accuracy (touch) */
}
@media (pointer: fine) {
    /* Device with accurate pointing (desktop, stylus-based) */
}
@media (pointer: none) {
    /* Device with no pointing */
}

Ejemplo para objetivos específicos:

@media (hover: none) and (pointer: coarse) {
    /* Smartphones and touchscreens */
}

@media (hover: hover) and (pointer: fine) {
    /* Desktops with mouse */
}

Me encantan los mixins, así es como uso mi hover mixin para apuntar solo a dispositivos que lo admiten:

@mixin on-hover {
    @media (hover: hover) and (pointer: fine) {
        &:hover {
            @content;
        }
    }
}

button {
    @include on-hover {
        color: blue;
    }
}
Calsal
fuente
eso no funciona en absoluto. tienes hover: ninguno en la consulta de medios para un mixin compatible con hover? necesita puntero: grueso según la otra respuesta aquí.
Allan Nienhuis
¿Lo has probado o solo dices que no funciona porque se ve raro? Es por eso que escribí "Se ve raro" .. Y funciona para mí sin puntero: grueso.
Calsal
Tuve algunos resultados inconsistentes con el enfoque CSS de esto en mi OnePlus 5T (tanto en Chrome como en Firefix). Esto funcionó bien en iOS, pero no pude hacerlo funcionar en OnePlus. Probé a fondo usando el puntero y el puntero
Zeth
6

Estoy lidiando con un problema similar actualmente.

Hay dos opciones principales que se me ocurren de inmediato: (1) comprobación de cadenas de usuario o (2) mantenimiento de páginas móviles separadas usando una URL diferente y hacer que los usuarios elijan lo que es mejor para ellos.

  1. Si puede usar un lenguaje de cinta adhesiva de Internet como PHP o Ruby, puede verificar la cadena de usuario del dispositivo que solicita una página, y simplemente mostrar el mismo contenido pero con un <link rel="mobile.css" />estilo diferente al normal.

Las cadenas de usuario tienen información de identificación sobre el navegador, el procesador, el sistema operativo, etc. Depende de usted decidir qué dispositivos son "táctiles" versus no táctiles. Es posible que pueda encontrar esta información disponible en algún lugar y asignarla a su sistema.

R. Si puede ignorar los navegadores antiguos , solo tiene que agregar una sola regla al CSS normal, no móvil, a saber: EDITAR : Erk. Después de experimentar un poco, descubrí que la siguiente regla también deshabilita la capacidad de seguir enlaces en los navegadores webkit, además de causar que los efectos estéticos se deshabiliten; consulte http://jsfiddle.net/3nkcdeao/
Como tal, tendrá para ser un poco más selectivo en cuanto a cómo modificar las reglas para el caso móvil que lo que muestro aquí, pero puede ser un punto de partida útil:

* { 
    pointer-events: none !important; /* only use !important if you have to */
}

Como nota al margen, deshabilitar los eventos de puntero en un elemento primario y luego habilitarlos explícitamente en un elemento secundario hace que los efectos de desplazamiento en el elemento primario se activen nuevamente si entra un elemento secundario :hover.
Ver http://jsfiddle.net/38Lookhp/5/

B. Si admite renderizadores web heredados, tendrá que trabajar un poco más en la línea de eliminar cualquier regla que establezca estilos especiales durante :hover. Para ahorrar tiempo a todos, es posible que solo desee crear un comando de copiado + sed automatizado que ejecute en sus hojas de estilo estándar para crear las versiones móviles. Eso le permitiría simplemente escribir / actualizar el código estándar y eliminar cualquier regla de estilo que use :hoverpara la versión móvil de sus páginas.

  1. (I) Alternativamente, simplemente informe a sus usuarios que tiene un m.website.com para dispositivos móviles además de su sitio web.com . Aunque el subdomaining es la forma más común, también podría tener alguna otra modificación predecible de una URL determinada para permitir que los usuarios móviles accedan a las páginas modificadas. En esa etapa, querrá asegurarse de que no tengan que modificar la URL cada vez que naveguen a otra parte del sitio.

De nuevo aquí, puede agregar una o dos reglas adicionales a las hojas de estilo o verse obligado a hacer algo un poco más complicado usando sed o una utilidad similar. Probablemente sería más fácil de aplicar: no a sus reglas de estilo como div:not(.disruptive):hover {...en las que agregaría class="disruptive"elementos que hacen cosas molestas para usuarios móviles que usan js o el idioma del servidor, en lugar de mezclar el CSS.

  1. (II) En realidad, puede combinar los dos primeros y (si sospecha que un usuario se ha desviado a la versión incorrecta de una página) puede sugerir que cambien dentro / fuera de la pantalla de tipo móvil, o simplemente tener un enlace en algún lugar permite a los usuarios pasar de un lado a otro. Como ya se indicó, las consultas de @media también podrían ser útiles para determinar qué se está visitando.

  2. (III) Si está preparado para una solución jQuery una vez que sepa qué dispositivos son "táctiles" y cuáles no, es posible que le resulte útil ignorar el desplazamiento del CSS en dispositivos con pantalla táctil .

RaramenteNeedy
fuente
pointer-events- ¡eso es increíble! Eso es exactamente lo que estaba buscando, ¡muchas gracias!
Simon Ferndriger
1
@SimonFerndriger Bueno, espero que sea útil en el futuro. Asegúrese de tener en cuenta la advertencia de que hasta la fecha, los <a>enlaces pueden ser inaccesibles en pointer-events:none. Con suerte, eso cambiará más tarde, o CSS 3.xo 4.0+ tendrá pointer-events:navigation-onlyo similar.
SeldomNeedy
6

prueba esto:

@media (hover:<s>on-demand</s>) {
    button:hover {
        background-color: #color-when-NOT-touch-device;
    }
}

ACTUALIZACIÓN: desafortunadamente, W3C ha eliminado esta propiedad de las especificaciones ( https://github.com/w3c/csswg-drafts/commit/2078b46218f7462735bb0b5107c9a3e84fb4c4b1 ).

Marouen Mhiri
fuente
De hecho, esto resolvería el problema. Nunca he oído hablar de esto antes. ¿En qué versión del navegador se supone que esto funciona?
Simon Ferndriger
3
@SimonFerndriger caniuse.com/#feat=css-media-interaction y algo más de información: dev.opera.com/articles/media-features
Escéptico
Bien ... No tiene un amplio soporte de navegador.
Giacomo Paita
¡Eso es genial! Gracias por una solución tan conveniente. Espero que se agregue a los estándares del W3C.
GProst
contento de poder ayudar ^^
Marouen Mhiri
5

Puede usar Modernizr JS (consulte también esta respuesta de StackOverflow ) o crear una función JS personalizada:

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

if ( is_touch_device() ) {
  $('html').addClass('touch');
} else {
  $('html').addClass('no-touch');
} 

para detectar el soporte del evento táctil en el navegador, y luego asignar una CSSpropiedad regular , atravesando el elemento con la html.no-touchclase, así:

html.touch a {
    width: 480px;
}

/* FOR THE DESKTOP, SET THE HOVER STATE */
html.no-touch a:hover {
   width: auto;
   color:blue;
   border-color:green;
}
Giacomo Paita
fuente
Eso también fue sugerido por @blade; sin embargo, usar la htmletiqueta en lugar de la bodyetiqueta puede hacer que sea un poco mejor leer. Esa es en realidad la solución que estoy usando actualmente. Gracias de todos modos.
Simon Ferndriger
1

Fue útil para mí: enlace

function hoverTouchUnstick() {
    // Check if the device supports touch events
    if('ontouchstart' in document.documentElement) {
        // Loop through each stylesheet
        for(var sheetI = document.styleSheets.length - 1; sheetI >= 0; sheetI--) {
            var sheet = document.styleSheets[sheetI];
            // Verify if cssRules exists in sheet
            if(sheet.cssRules) {
                // Loop through each rule in sheet
                for(var ruleI = sheet.cssRules.length - 1; ruleI >= 0; ruleI--) {
                    var rule = sheet.cssRules[ruleI];
                    // Verify rule has selector text
                    if(rule.selectorText) {
                        // Replace hover psuedo-class with active psuedo-class
                        rule.selectorText = rule.selectorText.replace(":hover", ":active");
                    }
                }
            }
        }
    }
}
mineroot
fuente
1

Esta también es una posible solución, pero tendrá que pasar por su CSS y agregar una .no-touchclase antes de sus estilos de desplazamiento.

Javascript:

if (!("ontouchstart" in document.documentElement)) {
document.documentElement.className += " no-touch";
}

Ejemplo de CSS:

<style>
p span {
    display: none;
}

.no-touch p:hover span {
    display: inline;
}
</style>
<p><a href="/">Tap me</a><span>You tapped!</span></p>

Fuente

Ps Pero debemos recordar que cada vez hay más dispositivos táctiles en el mercado, que también admiten la entrada del mouse al mismo tiempo.

mr.mii
fuente
0

Es posible que todavía no sea una solución perfecta (y es con jQuery), pero tal vez sea una dirección / concepto para trabajar: ¿qué hay de hacerlo al revés? Lo que significa desactivar los estados: hover css por defecto y activarlos si se detecta un evento mousemove en cualquier parte del documento. Por supuesto, esto no funciona si alguien desactiva js. ¿Qué más podría hablar en contra de hacerlo de esta manera?

Tal vez así:

CSS:

/* will only work if html has class "mousedetected" */
html.mousedetected a:hover {
   color:blue;
   border-color:green;
}

jQuery:

/* adds "mousedetected" class to html element if mouse moves (which should never happen on touch-only devices shouldn’t it?) */
$("body").mousemove( function() {
    $("html").addClass("mousedetected");
});
lars en upstruct
fuente
1
Un problema es que también es un .mousemove () al hacer clic en algo en dispositivos táctiles.
Lars en upstruct
1
Y probablemente desencadenará este evento sin parar, lo que tendrá un impacto en el rendimiento. Si esto funcionara, entonces debería usarlo mejor$('body').one.('mousemove', ...)
Simon Ferndriger
0

Pruebe esto (yo uso background y background-color en este ejemplo):

var ClickEventType = ((document.ontouchstart !== null) ? 'click' : 'touchstart');

if (ClickEventType == 'touchstart') {
            $('a').each(function() { // save original..
                var back_color = $(this).css('background-color');
                var background = $(this).css('background');
                $(this).attr('data-back_color', back_color);
                $(this).attr('data-background', background);
            });

            $('a').on('touchend', function(e) { // overwrite with original style..
                var background = $(this).attr('data-background');
                var back_color = $(this).attr('data-back_color');
                if (back_color != undefined) {
                    $(this).css({'background-color': back_color});
                }
                if (background != undefined) {
                    $(this).css({'background': background});
                }
            }).on('touchstart', function(e) { // clear added stlye="" elements..
                $(this).css({'background': '', 'background-color': ''});
            });
}

css:

a {
    -webkit-touch-callout: none;
    -webkit-tap-highlight-color: transparent;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
HU TanKy
fuente
0

La manera sucia ... No es elegante, pero es la forma más fácil que puede salvarte.

Elimine todo lo que caracterice el vuelo estacionario.

.your-class:hover:before {
  color: blue;
  background: linear-gradient(to bottom, rgba(231,56,39,0) 0%, #aaaaaa 100%);
}

@media all and (min-width:320px) and (max-width: 960px) {
    .your-class:hover:before {
    color: black;
    background: transparent;
  }
}
Fellipe Sanches
fuente
0

Si su problema es cuando toca / toca en Android y todo div cubierto por un color azul transparente. Entonces solo necesitas cambiar el

CURSOR: PUNTERO; al CURSOR: POR DEFECTO;

use mediaQuery para esconderse en el teléfono móvil / tableta.

Esto funciona para mi.

kunal Sharma
fuente
-1

Pruebe esta solución fácil de jquery 2019, aunque ha existido por un tiempo;

  1. agregue este complemento a la cabeza:

    src = "https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"

  2. agregue esto a js:

    $ ("*"). on ("touchend", función (e) {$ (this) .focus ();}); // se aplica a todos los elementos

  3. Algunas variaciones sugeridas a esto son:

    $ (": input,: checkbox,"). on ("touchend", function (e) {(this) .focus);}); // especifica elementos

    $ ("*"). on ("click, touchend", function (e) {$ (this) .focus ();}); // incluye evento click

    css: body {cursor: puntero; } // toca cualquier lugar para finalizar un foco

Notas

  • coloque el complemento antes de bootstrap.js para evitar afectar la información sobre herramientas
  • solo probado en iphone XR ios 12.1.12 y ipad 3 ios 9.3.5, usando Safari o Chrome.

Referencias

https://code.jquery.com/ui/

https://api.jquery.com/category/selectors/jquery-selector-extensions/

Darren
fuente
2
En 2019 no debería necesitar usar jQuery: el WebAPI es bastante potente y admite todas las características principales.
acme
Ya ... jQuery en 2019? ¿Qué tal un poco de Flash? Jejeje.
remed.io