javascript - instalar - Deshabilitar los efectos de desplazamiento en navegadores móviles
habilitar javascript en chrome en windows (16)
Estoy escribiendo un sitio web destinado a ser utilizado tanto en computadoras de escritorio como en tabletas. Cuando se visita desde un escritorio, quiero que las áreas de la pantalla en las que se puede hacer clic se iluminen con :hover
efectos de :hover
(diferente color de fondo, etc.) Con una tableta, no hay mouse, por lo que no quiero ningún efecto de desplazamiento.
El problema es que cuando toco algo en la tableta, el navegador evidentemente tiene una especie de "cursor invisible del mouse" que se mueve a la ubicación que toqué, y luego lo deja allí, así que lo que acabo de tocar se enciende con un Desplace el efecto hasta que toque algo más.
¿Cómo puedo obtener los efectos de desplazamiento cuando estoy usando el mouse, pero suprimirlos cuando estoy usando la pantalla táctil?
En caso de que alguien esté pensando en sugerirlo, no quiero usar el rastreo de agente de usuario. El mismo dispositivo podría tener tanto una pantalla táctil como un mouse (tal vez no tan común hoy, pero mucho más en el futuro). No estoy interesado en el dispositivo, estoy interesado en cómo se usa actualmente: mouse o pantalla táctil.
Ya intenté conectar los touchstart
, touchmove
y touchend
y llamar a preventDefault()
en todos ellos, lo que suprime el "cursor invisible del mouse" algunas veces; pero si toco rápidamente hacia adelante y hacia atrás entre dos elementos diferentes, después de algunos toques comenzará a mover el "cursor del mouse" e iluminará los efectos de desplazamiento de todos modos, es como si mi preventDefault
no siempre se respeta. No te aburriré con los detalles a menos que sea necesario; ni siquiera estoy seguro de que ese sea el enfoque correcto a seguir; si alguien tiene una solución más simple, soy todo oídos.
Editar: Esto se puede reproducir con CSS de pantano estándar :hover
, pero aquí hay una reproducción rápida para referencia.
<style>
.box { border: 1px solid black; width: 150px; height: 150px; }
.box:hover { background: blue; }
</style>
<div class="box"></div>
<div class="box"></div>
Si pasa el mouse sobre cualquiera de los cuadros, obtendrá un fondo azul, que yo quiero. Pero si tocas cualquiera de las casillas, también obtendrá un fondo azul, que es lo que intento evitar.
También publiqué una muestra here que hace lo anterior y también enlaza los eventos del mouse de jQuery. Puede usarlo para ver que los eventos de tap también dispararán mouseenter
, mousemove
y mouseleave
.
¿Cómo puedo obtener los efectos de desplazamiento cuando estoy usando el mouse, pero suprimirlos cuando estoy usando la pantalla táctil?
Tal vez no piense tanto en suprimir los efectos de desplazamiento de las pantallas táctiles, sino también en agregar efectos de desplazamiento para los eventos del mouse.
Si desea mantener los efectos :hover
en su CSS, puede especificar diferentes estilos para diferentes medios:
@media screen { /* hover styles here */ }
@media handheld { /* non-hover styles here */ }
Excepto que desafortunadamente hay muchos dispositivos móviles que ignoran esto y solo usan las reglas de la pantalla. Afortunadamente, muchos navegadores móviles / tabletas más nuevos admiten algunas consultas de medios más sofisticadas:
@media screen and (max-width:800px) { /* non-hover styles here */ }
Por lo tanto, incluso si se ignora la parte "pantalla" o "computadora de mano", el "ancho máximo" hará el truco para usted. Podría suponer que cualquier elemento con una pantalla de menos de 800 píxeles debe ser una tableta o un teléfono y no debe usar efectos de desplazamiento. Para los usuarios raros que usan un mouse en un dispositivo de baja resolución, no verían los efectos de desplazamiento pero su sitio estaría bien.
Lectura adicional sobre consultas en los medios? Hay muchos artículos sobre este en línea; aquí hay uno: http://www.alistapart.com/articles/return-of-the-mobile-stylesheet
Si cambias los efectos de desplazamiento de tu CSS y los aplicas con JavaScript, podrías vincularlos específicamente a los eventos del mouse, y / o simplemente podrías hacer algunas suposiciones basadas en el tamaño de la pantalla con el peor "problema" siendo que algunos el usuario que está usando un mouse pierde los efectos de desplazamiento.
Aquí está mi solución: http://jsfiddle.net/agamemnus/g56aw709/-- código a continuación.
Todo lo que uno tiene que hacer es convertir su ": hover" a ".hover" ... ¡eso es todo! La gran diferencia entre esto y el resto es que esto también funcionará en selectores de elementos no singulares como .my_class > *:hover {
.
handle_css_hover_effects ()
function handle_css_hover_effects (init) {
var init = init || {}
var handle_touch_events = init.handle_touch_events || true
var handle_mouse_events = init.handle_mouse_events || true
var hover_class = init.hover_class || "hover"
var delay_preferences = init.delay_preferences || {touch: {add: 500, remove: 500}}
function default_handler (curobj, input_type, op) {
var hovered_element_selector = "*" + ((op == "add") ? ":" : ("." + hover_class))
var hovered_elements = Array.prototype.slice.call(document.body.querySelectorAll(hovered_element_selector))
var modified_list = []
while (true) {
if ((curobj == null) || (curobj == document.documentElement)) break
if (hovered_elements.indexOf(curobj) != -1) modified_list.push (curobj)
curobj = curobj.parentNode
}
function do_hover_change () {modified_list.forEach (function (curobj) {curobj.classList[op](hover_class)})}
if ((!delay_preferences[input_type]) || (!delay_preferences[input_type][op])) {
do_hover_change ()
} else {
setTimeout (do_hover_change, delay_preferences[input_type][op])
}
}
if (handle_mouse_events) {
document.body.addEventListener (''mouseover'' , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "add")})
document.body.addEventListener (''mouseout'' , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "remove")})
document.body.addEventListener (''click'' , function (evt) {var curobj = evt.target; default_handler (curobj, "mouse", "remove")})
}
if (handle_touch_events) {
document.body.addEventListener (''touchstart'', function (evt) {var curobj = evt.target; default_handler (curobj, "touch", "add")})
document.body.addEventListener (''touchend'' , function (evt) {var curobj = evt.target; default_handler (curobj, "touch", "remove")})
document.body.addEventListener (''touchmove'', function (evt) {
var curobj = evt.target
var hovered_elements = Array.prototype.slice.call(document.body.querySelectorAll("*:hover"))
var lastobj = null
evt = evt.changedTouches[0]
var elements_at_point = get_elements_at_point (evt.pageX, evt.pageY)
// Get the last element that isn''t at the current point but is still hovered over, and remove only its hover attribute.
while (true) {
if ((curobj == null) || (curobj == document.documentElement)) break
if ((hovered_elements.indexOf(curobj) != -1) && (elements_at_point.indexOf(curobj) == -1)) lastobj = curobj
curobj = curobj.parentNode
}
if (lastobj == null) return
if ((!delay_preferences.touch) || (!delay_preferences.touch.remove)) {
lastobj.classList.remove(hover_class)
} else {
setTimeout (function () {lastobj.classList.remove(hover_class)}, delay_preferences.touch.remove)
}
function get_elements_at_point (x, y) {
var el_list = [], pe_list = []
while (true) {
var curobj = document.elementFromPoint(x, y)
if ((curobj == null) || (curobj == document.documentElement)) break
el_list.push (curobj); pe_list.push (curobj.style.pointerEvents)
curobj.style.pointerEvents = "none"
}
el_list.forEach (function (current_element, i) {current_element.style.pointerEvents = pe_list[i]})
return el_list
}
})
}
}
Bien, tuve un problema similar pero logré solucionarlo con consultas de medios y CSS simple. Estoy seguro de que estoy rompiendo algunas reglas aquí, pero está funcionando para mí.
Básicamente tuve que tomar una aplicación masiva que alguien hizo, y hacerla receptiva. Usaron jQueryUI y me pidieron que no alterara ninguna de sus jQuery, por lo que estaba restringido a usar CSS solo.
Cuando presioné uno de sus botones en el modo de pantalla táctil, el efecto de vuelo estacionario se dispararía por un segundo antes de que la acción del botón tuviese efecto. Así es como lo arreglé.
@media only screen and (max-width:1024px) {
#buttonOne{
height: 44px;
}
#buttonOne:hover{
display:none;
}
}
En mi proyecto, resolvimos este problema usando https://www.npmjs.com/package/postcss-hover-prefix y Modernizr Primero procesamos los archivos CSS de salida con postcss-hover-prefix
. Agrega .no-touch
para todas las reglas de css hover
.
const fs = require("fs");
const postcss = require("postcss");
const hoverPrfx = require("postcss-hover-prefix");
var css = fs.readFileSync(cssFileName, "utf8").toString();
postcss()
.use(hoverPrfx("no-touch"))
.process(css)
.then((result) => {
fs.writeFileSync(cssFileName, result);
});
css
a.text-primary:hover {
color: #62686d;
}
se convierte
.no-touch a.text-primary:hover {
color: #62686d;
}
En tiempo de ejecución, Modernizr
agrega automáticamente clases css a etiquetas html
como esta
<html class="wpfe-full-height js flexbox flexboxlegacy canvas canvastext webgl
no-touch
geolocation postmessage websqldatabase indexeddb hashchange
history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage
borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients
cssreflections csstransforms csstransforms3d csstransitions fontface
generatedcontent video audio localstorage sessionstorage webworkers
applicationcache svg inlinesvg smil svgclippaths websocketsbinary">
Tal postprocesamiento de css plus Modernizr desactiva el vuelo estacionario para dispositivos táctiles y permite otros. De hecho, este enfoque fue inspirado por Bootstrap 4, la forma en que resuelven el mismo problema: https://v4-alpha.getbootstrap.com/getting-started/browsers-devices/#sticky-hoverfocus-on-mobile
En un proyecto que hice recientemente, resolví este problema con la función de eventos delegados de jQuery. Busca ciertos elementos usando un selector jQuery y agrega / elimina una clase CSS a esos elementos cuando el mouse está sobre el elemento. Parece que funciona bien hasta donde he podido probarlo, que incluye IE10 en un portátil con capacidad táctil que ejecuta Windows 8.
$(document).ready(
function()
{
// insert your own selector here: maybe ''.hoverable''?
var selector = ''button, .hotspot'';
$(''body'')
.on(''mouseover'', selector, function(){ $(this).addClass(''mouseover''); })
.on(''mouseout'', selector, function(){ $(this).removeClass(''mouseover''); })
.on(''click'', selector, function(){ $(this).removeClass(''mouseover''); });
}
);
editar: esta solución, por supuesto, requiere que modifiques tu CSS para eliminar los selectores ": flotar" y contemplar de antemano qué elementos quieres que sean "aprovechables".
Si tiene muchos elementos en su página (como varios miles) puede ser un poco lento, porque esta solución detecta eventos de tres tipos en todos los elementos de la página y luego hace lo mismo si el selector coincide. Llamé a la clase CSS "mouseover" en lugar de "hover", porque no quería que ningún lector de CSS leyera ": hover" donde escribí ".hover".
Entiendo por tu pregunta que tu efecto de desplazamiento cambia el contenido de tu página. En ese caso, mi consejo es:
- Agregue efectos de
touchstart
entouchstart
ymouseenter
. - Elimine los efectos de
mouseleave
enmouseleave
,touchmove
yclick
.
Alternativamente, puede editar su página para que no haya cambios en el contenido.
Fondo
Para simular un mouse, los navegadores como Webkit mobile activan los siguientes eventos si un usuario toca y suelta un dedo en la pantalla táctil (como iPad) (fuente: Touch And Mouse en html5rocks.com):
-
touchstart
-
touchmove
-
touchend
- Retraso de 300 ms, donde el navegador se asegura de que se trata de un solo toque, no un doble toque
-
mouseover
-
mouseenter
- Nota : Si un evento
mouseover
,mouseenter
omousemove
cambia el contenido de la página, los siguientes eventos nunca se disparan.
- Nota : Si un evento
-
mousemove
-
mousedown
-
mouseup
-
click
No parece posible simplemente decirle al navegador web que se salte los eventos del mouse.
Lo que es peor, si un evento mouseover cambia el contenido de la página, el evento click nunca se activa, tal como se explica en Safari Web Content Guide - Manejo de eventos , en particular la figura 6.4 en One-Finger Events . Lo que es exactamente un "cambio de contenido" dependerá del navegador y la versión. He descubierto que para iOS 7.0, un cambio en el color de fondo no es (¿o ya no es más?) Un cambio de contenido.
Solución explicada
Recordar:
- Agregue efectos de
touchstart
entouchstart
ymouseenter
. - Elimine los efectos de
mouseleave
enmouseleave
,touchmove
yclick
.
Tenga en cuenta que no hay ninguna acción en touchend
!
Esto funciona claramente para eventos de mouse: mouseenter
y mouseleave
(versiones ligeramente mejoradas de mouseover
y mouseout
) se disparan, y agregan y eliminan el elemento emergente.
Si el usuario realmente hace click
un enlace, también se elimina el efecto de desplazamiento. Esto asegura que se elimine si el usuario presiona el botón Atrás en el navegador web.
Esto también funciona para eventos táctiles: en touchstart se agrega el efecto de desplazamiento. No se '''' ''elimina'' ''en touchend. Se vuelve a mouseenter
en el mouseenter
y, dado que esto no provoca cambios en el contenido (ya se agregó), también se mouseenter
el evento click
y se sigue el enlace sin necesidad de que el usuario vuelva a hacer clic.
La demora de touchstart
tiene un navegador entre un evento de touchstart
y un click
realmente se usa porque el efecto de desplazamiento se mostrará durante este corto tiempo.
Si el usuario decide cancelar el clic, un movimiento del dedo lo hará igual de normal. Normalmente, esto es un problema ya que no se mouseleave
ningún evento mouseleave
, y el efecto de mouseleave
permanece en su lugar. Afortunadamente, esto se puede solucionar fácilmente eliminando el efecto de touchmove
sobre el touchmove
.
¡Eso es!
Tenga en cuenta que es posible eliminar el retraso de 300 ms , por ejemplo, utilizando la biblioteca de FastClick , pero esto está fuera del alcance de esta pregunta.
Soluciones alternativas
He encontrado los siguientes problemas con las siguientes alternativas:
- detección de navegador: extremadamente propenso a errores. Asume que un dispositivo tiene mouse o touch, mientras que una combinación de ambos se volverá cada vez más común cuando touch exhiba prolifirate.
- Detección de medios CSS: la única solución exclusiva de CSS que conozco. Todavía es propenso a errores y aún asume que un dispositivo tiene mouse o touch, mientras que ambos son posibles.
- Emule el evento click en
touchend
: Esto seguirá incorrectamente el enlace, incluso si el usuario solo quería desplazarse o hacer zoom, sin la intención de hacer clic en el enlace. - Use una variable para suprimir los eventos del mouse: esto establece una variable en
touchend
que se usa como condición if en eventos de mouse posteriores para evitar cambios de estado en ese momento. La variable se restablece en el evento click. Vea la respuesta de Walter Roman en esta página. Esta es una solución decente si realmente no desea un efecto de desplazamiento sobre las interfaces táctiles. Desafortunadamente, esto no funciona si se dispara untouchend
por otro motivo y no setouchend
ningún clic (por ejemplo, el usuario desplazado o ampliado), y posteriormente intenta seguir el enlace con un mouse (es decir, en un dispositivo con mouse y touch interfaz).
Otras lecturas
- http://jsfiddle.net/macfreek/24Z5M/ . Pruebe la solución anterior para usted mismo en esta caja de arena.
- http://www.macfreek.nl/memory/Touch_and_mouse_with_hover_effects_in_a_web_browser . Esta misma respuesta, con un poco más de fondo.
- https://www.html5rocks.com/en/mobile/touchandmouse/ . Gran artículo de fondo sobre html5rocks.com sobre el tacto y el mouse en general.
- https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html . Guía de contenido web de Safari: manejo de eventos. Ver en particular la figura 6.4, que explica que no se disparan más eventos después de un cambio de contenido durante un evento
mouseover
omousemove
.
Escribí el siguiente JS para un proyecto reciente, que era un sitio de escritorio / móvil / tableta que tiene efectos de desplazamiento que no deberían aparecer al tocar.
El módulo mobileNoHoverState
continuación tiene una variable preventMouseover
(inicialmente declarada como false
), que se establece en true
cuando un usuario touchstart
evento touchstart
en un elemento, $target
.
preventMouseover
se vuelve a establecer como false
siempre que se mouseover
evento mouseover
, lo que permite que el sitio funcione de la manera prevista si el usuario usa tanto la pantalla táctil como el mouse.
Sabemos que mouseover
se está touchstart
después de la entrada touchstart
debido al orden en que se declaran dentro de init
.
var mobileNoHoverState = function() {
var hoverClass = ''hover'',
$target = $(".foo"),
preventMouseover = false;
function forTouchstart() {
preventMouseover = true;
}
function forMouseover() {
if (preventMouseover === false) {
$(this).addClass(hoverClass);
} else {
preventMouseover = false;
}
}
function forMouseout() {
$(this).removeClass(hoverClass);
}
function init() {
$target.on({
touchstart : forTouchstart,
mouseover : forMouseover,
mouseout : forMouseout
});
}
return {
init: init
};
}();
El módulo se instancia más adelante en la línea:
mobileNoHoverState.init();
Hola persona del futuro, es probable que desee utilizar el pointer
y / o la consulta de medios flotantes. La consulta de medios de handheld
estaba obsoleta.
/* device is using a mouse or similar */
@media (pointer: fine) {
a:hover {
background: red;
}
}
Incluye Modernizr en tu página y establece tus estados de desplazamiento como este en su lugar:
html.no-touchevents .box:hover {
background: blue;
}
Iv había encontrado 2 soluciones al problema, lo que implicaba que detectaba touch con modernizr o alguna otra cosa y configuraba una clase táctil en el elemento html.
Esto es bueno pero no se supported muy bien:
html.touch *:hover {
all:unset!important;
}
Pero esto tiene un muy buen support :
html.touch *:hover {
pointer-events: none !important;
}
Funciona de manera impecable para mí, hace que todos los efectos de desplazamiento sean como cuando tienes un toque en un botón que se iluminará pero no terminará con errores como el efecto de vuelo estacionario inicial para los eventos del mouse.
Detectando el tacto desde dispositivos sin contacto, creo que Modernizr ha hecho el mejor trabajo:
https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js
EDITAR
Encontré una solución mejor y más simple para este problema
Lo que he hecho para resolver el mismo problema es tener una detección de características (uso algo como este código ), ver si onTouchMove está definido, y si es así, agrego la clase css "touchMode" al cuerpo, sino agrego " desktopMode ".
Luego, cada vez que un efecto de estilo solo se aplica a un dispositivo táctil, o solo a un escritorio, la regla css se antepone con la clase apropiada:
.desktopMode .someClass:hover{ color: red }
.touchMode .mainDiv { width: 100%; margin: 0; /*etc.*/ }
Editar : Esta estrategia, por supuesto, agrega algunos caracteres adicionales a su CSS, por lo que si le preocupa el tamaño CSS, puede buscar las definiciones touchMode y desktopMode y colocarlas en diferentes archivos, para que pueda servir CSS optimizado para cada dispositivo tipo; o puede cambiar los nombres de clase a algo mucho más corto antes de ir a prod.
Mi solución es agregar la clase css activada por desplazamiento a la etiqueta HTML, y usarla al comienzo de todos los selectores de CSS con: hover y eliminar esa clase en el primer evento touchstart.
http://codepen.io/Bnaya/pen/EoJlb
JS:
(function () {
''use strict'';
if (!(''addEventListener'' in window)) {
return;
}
var htmlElement = document.querySelector(''html'');
function touchStart () {
document.querySelector(''html'').classList.remove(''hover-active'');
htmlElement.removeEventListener(''touchstart'', touchStart);
}
htmlElement.addEventListener(''touchstart'', touchStart);
}());
HTML:
<html class="hover-active">
CSS:
.hover-active .mybutton:hover {
box-shadow: 1px 1px 1px #000;
}
Puede disparar el evento mouseLeave
siempre que toque un elemento en la pantalla táctil. Aquí hay una solución para todas las etiquetas <a>
:
function removeHover() {
var anchors = document.getElementsByTagName(''a'');
for(i=0; i<anchors.length; i++) {
anchors[i].addEventListener(''touchstart'', function(e){
$(''a'').mouseleave();
}, false);
}
}
Puede ser útil ver tu CSS, ya que parece ser un problema bastante extraño. Pero de todos modos, si está sucediendo y todo lo demás está bien, podrías intentar cambiar el efecto de desplazamiento a javascript (también puedes usar jquery). Simplemente, agréguese al mouseover o mejor aún al evento mouseenter e ilumine su elemento cuando se desate el evento.
Consulte el último ejemplo aquí: http://api.jquery.com/mouseover/ , ¡podría usar algo similar para iniciar sesión cuando el evento se active y llevarlo desde allí!
Realmente quería una solución de css
pura para esto, ya que rociar una solución de javascript de gran peso en todos mis puntos de vista parecía una opción desagradable. Finalmente encontré la consulta @media.hover , que puede detectar "si el mecanismo de entrada principal le permite al usuario pasar el mouse sobre los elementos". Esto evita los dispositivos táctiles donde "flotar" es más una acción emulada que una capacidad directa del dispositivo de entrada.
Entonces, por ejemplo, si tengo un enlace:
<a href="/" class="link">Home</a>
Luego puedo modelarlo de manera segura :hover
el :hover
cuando el dispositivo lo admite fácilmente con este css
:
@media (hover: hover) {
.link:hover { /* hover styles */ }
}
Si bien la mayoría de los navegadores modernos admiten consultas de funciones de medios de interacción, algunos navegadores populares como IE y Firefox no lo hacen. En mi caso, esto funciona bien, ya que solo tenía la intención de admitir Chrome en el escritorio y Chrome y Safari en el móvil.
Si está contento de usar JavaScript, puede usar Modernizr en su página. Cuando se carga la página, un navegador de pantalla no táctil tendrá la clase ''.no-touch'' agregado a la etiqueta html, pero para un navegador de pantalla táctil, la etiqueta html tendrá la clase ''.touch'' agregada a la etiqueta html .
Entonces, simplemente es un caso de verificar si la etiqueta html tiene la clase sin toque antes de decidir agregar el mouseenter y los detectores de mouseleave.
if($(''html'').hasClass(''no-touch'')){
$(''.box'').on("mouseenter", function(event){
$(this).css(''background-color'',''#0000ff'')
});
$(''.box'').on("mouseleave", function(event){
$(this).css(''background-color'','''')
});
}
Para un dispositivo con pantalla táctil, los eventos no tendrán oyentes, por lo que no obtendrás ningún efecto de desplazamiento al tocar.