: la pseudoclase activa no funciona en safari móvil

109

En Webkit en iPhone / iPad / iPod, especificar el estilo de una pseudoclase activa para una <a>etiqueta no se activa cuando toca el elemento. ¿Cómo puedo hacer que esto se active? Código de ejemplo:

<style> 
a:active { 
    background-color: red;
}
</style>
<!-- snip -->
<a href="#">Click me</a>
Jesse Rusak
fuente

Respuestas:

238
<body ontouchstart="">
    ...
</body>

Aplicado solo una vez, a diferencia de cada elemento de botón, parecía arreglar todos los botones de la página. Alternativamente, puede usar esta pequeña biblioteca JS llamada ' Fastclick '. Acelera los eventos de clic en dispositivos táctiles y también se ocupa de este problema.

wilsonpage
fuente
19
¿No es suficiente simplemente agregar ontouchstartsin el =""? (HTML5)
feklee
2
Para futuros lectores, he publicado una demostración aquí: http://jsbin.com/EjiWILe/3/ . Verifique la funcionalidad en su dispositivo iOS.
mhulse
6
¿Puede explicar por qué ha aplicado un oyente vacío al cuerpo? ¿Cómo funciona?
Maurizio Battaghini
2
Estaba usando: focus, y esto también funciona para eso. Gracias por la magia.
TecBrat
Obtengo congelaciones de página en iOS 9.1 y 9.3 usando este método ... esas versiones tenían un error js, pero todavía tienen un 0.5% de usuarios en esos sistemas operativos
Ruskin
55

Como han dicho otras respuestas, iOS Safari no activa la :activepseudoclase a menos que se adjunte un evento táctil al elemento, pero hasta ahora este comportamiento ha sido "mágico". Encontré esta pequeña propaganda en la Biblioteca de desarrolladores de Safari que lo explica (el énfasis es mío):

También puede usar la -webkit-tap-highlight-colorpropiedad CSS en combinación con la configuración de un evento táctil para configurar los botones para que se comporten de manera similar al escritorio. En iOS, los eventos del mouse se envían tan rápido que nunca se recibe el estado inactivo o activo. Por lo tanto, el :activepseudoestado se activa solo cuando hay un evento táctil establecido en el elemento HTML, por ejemplo, cuando ontouchstart está configurado en el elemento de la siguiente manera:

<button class="action" ontouchstart=""
        style="-webkit-tap-highlight-color: rgba(0,0,0,0);">
    Testing Touch on iOS
</button>

Ahora, cuando se toca y se mantiene presionado el botón en iOS, el botón cambia al color especificado sin que aparezca el color gris transparente circundante.

En otras palabras, configurar un ontouchstartevento (incluso si está vacío) le dice explícitamente al navegador que reaccione a los eventos táctiles.

En mi opinión, este es un comportamiento defectuoso y probablemente se remonta a la época en que la web "móvil" era básicamente inexistente (eche un vistazo a esas capturas de pantalla en la página vinculada para ver a qué me refiero), y todo estaba orientado al mouse. Es interesante observar que otros navegadores móviles más nuevos, como los de Android, muestran el pseudo-estado `: active 'en el tacto muy bien, sin ningún tipo de trucos como los que se necesitan para iOS.

(Nota al margen: si desea usar sus propios estilos personalizados en iOS, también puede deshabilitar el cuadro gris translúcido predeterminado que usa iOS en lugar del :activepseudo-estado usando la -webkit-tap-highlight-colorpropiedad CSS, como se explica en la misma página vinculada arriba .)


Después de un poco de experimentación, la solución esperada de configurar un ontouchstartevento en el <body>elemento al que todos los eventos táctiles luego burbujean no funciona completamente. Si el elemento está visible en la ventana gráfica cuando se carga la página, entonces funciona bien, pero desplazarse hacia abajo y tocar un elemento que estaba fuera de la ventana gráfica no activa el :activepseudoestado como debería. Entonces, en lugar de

<!DOCTYPE html>
<html><body ontouchstart></body></html>

adjunte el evento a todos los elementos en lugar de confiar en el evento que se propaga al cuerpo (usando jQuery):

$('body *').on('touchstart', function (){});

Sin embargo, no estoy al tanto de las implicaciones de rendimiento de esto, así que tenga cuidado.


EDITAR: Hay una falla seria con esta solución: incluso tocar un elemento mientras se desplaza por la página activará el :activepseudoestado. La sensibilidad es demasiado fuerte. Android resuelve esto introduciendo una pequeña demora antes de que se muestre el estado, que se cancela si se desplaza la página. A la luz de esto, sugiero usar esto solo en elementos seleccionados. En mi caso, estoy desarrollando una aplicación web para usar en el campo que es básicamente una lista de botones para navegar por las páginas y enviar acciones. Debido a que la página completa es prácticamente botones en algunos casos, esto no funcionará para mí. Sin embargo, puede configurar el :hoverpseudo-estado para que lo complete. Después de desactivar el cuadro gris predeterminado, esto funciona perfectamente.

user128216
fuente
6
¡Gran explicación del por qué y no solo del cómo ! ¡Gracias!
coderfin
6
Votado a favor por brindarles a los desarrolladores comprensión, no solo una receta "mágica" Esto es infinitamente más informativo que los demás; gracias por escribir esto para nosotros.
user508633
Voto a favor para una explicación detallada + advertencias. Esta debería ser la respuesta correcta.
Master of Ducks
39

Agregue un controlador de eventos para ontouchstart en su etiqueta <a>. Esto hace que el CSS funcione mágicamente.

<a ontouchstart="">Click me</a>
Jesse Rusak
fuente
3
Lo siento, esta es una respuesta antigua, pero ¿por qué funciona? No me importa "mágicamente" como razón, pero si pudiera explicar por qué funciona, me encantaría leer más detalles.
mhulse
1
@mhulse También me encantaría saber por qué.
Jesse Rusak
¡Decir ah! Estamos en el mismo barco. Intentaré investigar un poco y publicar aquí un enlace a documentos (semi) oficiales sobre el tema. Me encanta que esta técnica funcione, solo me pregunto por qué.
mhulse
7

Esto funciona para mi:

document.addEventListener("touchstart", function() {},false);

Nota: si hace este truco, también vale la pena eliminar el color de toque y resaltado predeterminado que aplica Mobile Safari usando la siguiente regla CSS.

html {
    -webkit-tap-highlight-color: rgba(0,0,0,0);
}
dhqvinh
fuente
5

A partir del 8 de diciembre de 2016, la respuesta aceptada ( <body ontouchstart="">...</body>) no me funciona en Safari 10 (iPhone 5s): ese truco solo funciona para aquellos elementos que eran visibles al cargar la página.

Sin embargo, agregando:

<script type='application/javascript'>
  document.addEventListener("touchstart", function() {}, false);
</script>

la headhace funcionar de la manera que quiero, con la desventaja de que ahora todos los eventos de toque durante el desplazamiento también desencadenan la :activepseudo-estado en los elementos tocados. (Si esto es un problema para usted, podría considerar la :hover solución alternativa de FighterJet ).

Ali
fuente
4
//hover for ios
-webkit-tap-highlight-color: #ccc;

Esto funciona para mí, agregue a su CSS en el elemento que desea resaltar

Ojo rojo
fuente
3

¿Estás usando todas las pseudoclases o solo una? Si está usando al menos dos, asegúrese de que estén en el orden correcto o todos se romperán:

a:link
a:visited
a:hover
a:active

..en ese orden. Además, si solo está usando: active, agregue un enlace:, incluso si no lo está modificando.

tahdhaze09
fuente
un gran consejo sobre la importancia del orden al diseñar pseudoclases.
brianarpie
1

He publicado una herramienta que debería solucionar este problema.

A primera vista, el problema parece simple, pero en realidad el comportamiento de tocar y hacer clic debe personalizarse bastante, incluidas las funciones de tiempo de espera y cosas como "qué sucede cuando se desplaza por una lista de vínculos" o "qué sucede cuando presiona el vínculo y luego alejar el mouse / dedo del área activa "

Esto debería resolverlo todo a la vez: https://www.npmjs.com/package/active-touch

Deberá tener sus: estilos activos asignados a la clase .active o elegir su propio nombre de clase. De forma predeterminada, el script funcionará con todos los elementos del enlace, pero puede sobrescribirlo con su propia matriz de selectores.

¡Comentarios y contribuciones honestas y útiles muy apreciadas!

dmitrizzle
fuente
2
Esa biblioteca agrega 9 oyentes de eventos (no pasivos) a cada elemento de enlace, pero no llama a removeEventListener; creo que esto sería un gran defecto para las aplicaciones de una sola página
Drenai
1
Gracias Ryan. No he estado apoyando ni usando este código durante más de un año; Lo quitaré. Después de pensarlo un poco y experimentar, cambié el diseño de la aplicación en lugar de intentar forzar interacciones que los navegadores no tenían en cuenta o pensaban.
dmitrizzle
1

Para aquellos que no quieran usar ontouchstart, pueden usar este código

<script>
 document.addEventListener("touchstart", function(){}, true);
</script>
Desarrollador novato
fuente
1

Probé esta respuesta y sus variantes, pero ninguna parecía funcionar de manera confiable (y no me gusta confiar en la 'magia' para cosas como esta). Así que hice lo siguiente en su lugar, que funciona perfectamente en todas las plataformas, no solo en Apple:

  1. Declaraciones CSS renombradas que solían :activehacerlo .active.
  2. Hizo una lista de todos los elementos afectados y se agregaron pointerdown/mousedown/touchstartcontroladores de eventos para aplicar la .activeclase y los pointerup/mouseup/touchendcontroladores de eventos para eliminarlos. Usando jQuery:

    let controlActivationEvents = window.PointerEvent ? "pointerdown" : "touchstart mousedown";
    let controlDeactivationEvents = window.PointerEvent ? "pointerup pointerleave" : "touchend mouseup mouseleave";
    
    let clickableThings = '<comma separated list of selectors>';
    $(clickableThings).on(controlActivationEvents,function (e) {
        $(this).addClass('active');
    }).on(controlDeactivationEvents, function (e) {
        $(this).removeClass('active');
    });

Esto fue un poco tedioso, pero ahora tengo una solución que es menos vulnerable a la rotura entre las versiones del sistema operativo Apple. (¿Y quién necesita algo como esto para romper?)

daffinm
fuente
Creo que proporciona una respuesta a la pregunta. Parece que no hay una manera confiable de hacer que la pseudoclase activa funcione en Safari móvil. Pero esto se puede emular de manera confiable usando la solución que he dado. He hecho esto en mi aplicación y funciona perfectamente en todas las plataformas. Y he añadido código a modo de aclaración.
daffinm
0

Una solución es confiar en en :targetlugar de :active:

<style> 
a:target { 
    background-color: red;
}
</style>
<!-- snip -->
<a id="click-me" href="#click-me">Click me</a>

El estilo se activará cuando la URL actual apunte al ancla, que es sólida incluso en dispositivos móviles. El inconveniente es que necesita otro enlace para borrar el ancla en la URL. Ejemplo completo:

a:target { 
    background-color: red;
}
<a id="click-me" href="#click-me">Click me</a>
<a id="clear" href="#">Clear</a>

rgmt
fuente
-3

No está 100% relacionado con esta pregunta, pero también puede usar css sibling hack para lograr esto

HTML

<input tabindex="0" type="checkbox" id="145"/>
<label for="145"> info</label>
<span> sea</span>

SCSS

input {
    &:checked + label {
      background-color: red;
    }
  }

Si desea utilizar información sobre herramientas html / css pura

span {
  display: none;
}
input {
    &:checked ~ span {
      display: block;
    }
  }
Seeliang
fuente
-6
<!DOCTYPE html>

<html lang="en">

<head>
<meta charset="utf-8">

<title></title>

<style>
        a{color: red;}
        a:hover{color: blue;}
</style>
</head>

<body>

        <div class="main" role="main">
                <a href="#">Hover</a>
        </div>

</body>
</html>
TroyM
fuente
1
Según las preguntas frecuentes, las firmas y la autopromoción no están permitidas.
LittleBobbyTables - Au Revoir