¿Cómo simulo un desplazamiento con un toque en navegadores habilitados para tocar?

120

Con algo de HTML como este:

<p>Some Text</p>

Luego, algo de CSS como este:

p {
  color:black;
}

p:hover {
  color:red;
}

¿Cómo puedo permitir un toque prolongado en un dispositivo táctil para replicar el desplazamiento? Puedo cambiar el marcado / usar JS, etc., pero no puedo pensar en una manera fácil de hacer esto.

Rich Bradshaw
fuente
Estoy pensando específicamente en iPhone OS: es una demostración de animación CSS en la que, para muchos, es fácil usar el mouse en lugar de hacer clic para comenzar.
Rich Bradshaw

Respuestas:

172

¡Bien, lo he resuelto! Implica cambiar ligeramente el CSS y agregar algo de JS.

Usando jQuery para hacerlo más fácil:

$(document).ready(function() {
    $('.hover').on('touchstart touchend', function(e) {
        e.preventDefault();
        $(this).toggleClass('hover_effect');
    });
});

En inglés: cuando comienzas o terminas un toque, enciende hover_effecto apaga la clase .

Luego, en su HTML, agregue una clase flotante a cualquier cosa con la que desee que esto funcione. En su CSS, reemplace cualquier instancia de:

element:hover {
    rule:properties;
}

con

element:hover, element.hover_effect {
    rule:properties;
}

Y solo para mayor utilidad, agregue esto a su CSS también:

.hover {
-webkit-user-select: none;
-webkit-touch-callout: none;        
}

Para detener el navegador pidiéndole que copie / guarde / seleccione la imagen o lo que sea.

¡Fácil!

Rich Bradshaw
fuente
Esto es genial. Sin embargo, dividí la función domready porque toggleestropearía las cosas si el usuario toca un elemento y arrastra a otro (por ejemplo, si tocó el elemento incorrecto y está tratando de cancelar el toque)
Jacksonkr
Eliminé touchendy preventDefault()en mi sitio web y funciona bien, pero ahora los menús no se cierran automáticamente al tocar fuera del objetivo.
Даниил Пронин
También me gustaría recibir algunos consejos sobre cómo cerrar ese menú cuando el usuario toca en otro lugar o comienza a desplazarse. Supongo que podría haber otro touchstartoyente en el cuerpo (o similar) pero me preguntaba si hay una mejor manera. ¡Gracias!
Darryl Young
2
¿Hay alguna forma de ignorar un gesto de desplazamiento? Esto hace exactamente lo que necesito, pero ahora no puedo desplazarme hacia arriba y hacia abajo por el contenido que tiene este efecto.
alsobubbly
1
Eliminé preventDefault ya que quiero que el desplazamiento esté disponible, pero gracias por los consejos sobre touchstart y touchend. Dios envíe, esta es la única respuesta que funciona de manera confiable en todos los navegadores.
Petraeus
54

Todo lo que necesita hacer es vincular touchstart en un padre. Algo como esto funcionará:

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

No necesita hacer nada en la función, déjela vacía. Esto será suficiente para que los desplazamientos estén al tacto, por lo que un toque se comporta más como: desplazarse y menos como: activo. Magia de iOS.

Razvan Cercelaru
fuente
2
¿Esta solución permite que el enlace se ejecute en el segundo toque?
samuelkobe
1
No, dispara el clic en el primer toque todavía. Lo acabo de probar.
Jack
¡Excelente solución! Rapido y Facil.
Hunter Turner
41

Prueba esto:

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

Y en tu CSS:

element:hover, element:active {
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-user-select: none;
-webkit-touch-callout: none /*only to disable context menu on long press*/
}

¡Con este código no necesitas una clase .hover adicional!

Anselmo
fuente
1
esto funciona para mí ... algunas otras cosas que podrían ayudarlo aún más es en lugar de agregar javascript, haga esto: <body ontouchstart = ""> luego agregue un pequeño temporizador en el evento css: active: tr: active {background-color : # 118aab; transición: todo .2s lineal; -webkit-tap-highlight-color: rgba (0,0,0,0); -webkit-user-select: ninguno; -webkit-touch-callout: none}
Kozy
Trabajó para mi. Gracias ... Lo único - se retrasa un poco ... quiero decir - después de presionar, no se carga instantáneamente.
Roman Losev
Use fastclick.js para eliminar el retraso
Anselm
25

Para responder a su pregunta principal: "¿Cómo puedo simular un desplazamiento con un navegador táctil habilitado?"

Simplemente permita 'hacer clic' en el elemento (tocando la pantalla) y luego active el hoverevento usando JavaScript.

var p = document.getElementsByTagName('p')[0];
p.onclick = function() {
 // Trigger the `hover` event on the paragraph
 p.onhover.call(p);
};

Esto debería funcionar, siempre que haya un hoverevento en su dispositivo (aunque normalmente no se usa).

Actualización: acabo de probar esta técnica en mi iPhone y parece funcionar bien. Pruébelo aquí: http://jsfiddle.net/mathias/YS7ft/show/light/

Si desea usar un 'toque largo' para activar el desplazamiento, puede usar el fragmento de código anterior como punto de partida y divertirse con temporizadores y demás;)

Mathias Bynens
fuente
No sabía sobre ese bit onhover.call (p), eso es bastante bueno. Sin embargo, no hay vuelo fuera de ...
Rich Bradshaw
¿Ha probado esto con múltiples elementos y toque prolongado? ¿Significa que el usuario mueve su dedo por la pantalla y muestra un estilo de desplazamiento para cada elemento?
Marcus
Parece que si ya está usando jQuery, es posible que pueda llamar al evento de desplazamiento con jQuery en sí, por ejemplo: jQuery.hover (); Sin embargo, no estoy seguro de que la API lo admita.
Kzqai
Vaya, ese fue un comentario más aplicable a la publicación anterior que en realidad usa jQuery, lo siento.
Kzqai
¿Necesita tener alguna secuencia de comandos especial o llamar a la función? Me funciona, pero solo en una página y no sé cuál es la diferencia.
Richard Young
13

Solución mejorada adicional

Primero fui con el enfoque de Rich Bradshaw , pero luego empezaron a aparecer problemas. Al hacer e.preventDefault () en el evento 'touchstart' , la página ya no se desplaza y, ni la pulsación prolongada puede activar el menú de opciones ni el zoom con doble clic puede finalizar la ejecución.

Una solución podría ser averiguar qué evento se está llamando y simplemente e.preventDefault () en el último, 'tocar' . Dado que el 'touchmove' de scroll viene antes de 'touchend' , permanece como de forma predeterminada, y el 'clic' también se evita, ya que viene después de las palabras posteriores en la cadena de eventos aplicada a dispositivos móviles, así:

// Binding to the '.static_parent' ensuring dynamic ajaxified content
$('.static_parent').on('touchstart touchend', '.link', function (e) {

    // If event is 'touchend' then...
    if (e.type == 'touchend') {
        // Ensuring we event prevent default in all major browsers
        e.preventDefault ? e.preventDefault() : e.returnValue = false;
    }

    // Add class responsible for :hover effect
    $(this).toggleClass('hover_effect');
});

Pero luego, cuando aparece el menú de opciones, ya no activa el 'toque' responsable de alternar la clase, y la próxima vez el comportamiento de desplazamiento será al revés, totalmente confuso.

Entonces, una solución sería, nuevamente, descubrir condicionalmente en qué evento estamos, o simplemente hacer eventos separados, y usar addClass () y removeClass () respectivamente en 'touchstart' y 'touchend' , asegurándose de que siempre comience y termine por respectivamente agregando y quitando en lugar de decidir condicionalmente sobre ello. Para finalizar, también vincularemos la devolución de llamada de eliminación al tipo de evento 'focusout' , siendo responsables de borrar la clase de desplazamiento de cualquier enlace que pueda permanecer encendida y nunca volver a visitarse, así:

$('.static_parent').on('touchstart', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('touchend focusout', '.link', function (e) {
    // Think double click zoom still fails here
    e.preventDefault ? e.preventDefault() : e.returnValue = false;
    $(this).removeClass('hover_effect');
});

Atención: Todavía se producen algunos errores en las dos soluciones anteriores y, piense también (no probado), el zoom de doble clic también falla.

Solución de Javascript ordenada y, con suerte, libre de errores (no :))

Ahora , por un segundo enfoque, más limpio, ordenado y receptivo, simplemente usando javascript (sin mezcla entre la clase .hover y pseudo: hover) y desde donde puede llamar directamente su comportamiento ajax en el evento universal (móvil y escritorio) 'clic' , Encontré una pregunta bastante bien respondida de la cual finalmente entendí cómo podía mezclar eventos táctiles y de mouse sin que varias devoluciones de llamada de eventos inevitablemente cambiaran las de los demás en la cadena de eventos. Así es cómo:

$('.static_parent').on('touchstart mouseenter', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('mouseleave touchmove click', '.link', function (e) {
    $(this).removeClass('hover_effect');

    // As it's the chain's last event we only prevent it from making HTTP request
    if (e.type == 'click') {
        e.preventDefault ? e.preventDefault() : e.returnValue = false;

        // Ajax behavior here!
    }
});
Alexandre Rosário
fuente
3

El hoverefecto del mouse no se puede implementar en un dispositivo táctil. Cuando aparecí con la misma situación en safari iosque usé :activeen CSS para hacer efecto.

es decir.

p:active {
  color:red;
}

En mi caso, está funcionando. Puede ser que este también sea el caso que se pueda usar sin usar javascript. Pruébalo.

sijo vijayan
fuente
2

Agregue este código y luego configure la clase "tapHover" a los elementos que le gustaría que funcionen de esta manera. La primera vez que toque un elemento, obtendrá la pseudoclase ": hover" y la clase "tapped". Se evitará el evento de clic. La segunda vez que toque el mismo elemento, se activará el evento de clic.

// Activate only in devices with touch screen
if('ontouchstart' in window)
{
    // this will make touch event add hover pseudoclass
    document.addEventListener('touchstart', function(e) {}, true);

    // modify click event
    document.addEventListener('click', function(e) {
        // get .tapHover element under cursor
        var el = jQuery(e.target).hasClass('tapHover')
            ? jQuery(e.target)
            : jQuery(e.target).closest('.tapHover');

        if(!el.length)
            return;

        // remove tapped class from old ones
        jQuery('.tapHover.tapped').each(function() {
            if(this != el.get(0))
                jQuery(this).removeClass('tapped');
        });

        if(!el.hasClass('tapped'))
        {
            // this is the first tap
            el.addClass('tapped');
            e.preventDefault();
            return false;
        }
        else
        {
            // second tap
            return true;
        }
    }, true);
}
.box {
	float:		left;
	
	display:	inline-block;
	margin:		50px 0 0 50px;
	width:		100px;
	height:		100px;
	overflow:	hidden;
	
	font-size:	20px;
	
	border:		solid 1px black;
}
.box.tapHover {
	background:	yellow;
}
.box.tapped {
	border:		solid 3px red;
}
.box:hover {
	background:	red;
}
<div class="box" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>

l00k
fuente
1

Sin JS específico del dispositivo (o más bien del navegador), estoy bastante seguro de que no tendrá suerte.

Editar: pensé que querías evitar eso hasta que volviera a leer tu pregunta. En el caso de Mobile Safari, puede registrarse para obtener todos los eventos táctiles similares a los que puede hacer con UIView-s nativos. No puedo encontrar la documentación en este momento, aunque lo intentaré.

Joonas Trussmann
fuente
1

Una forma de hacerlo sería hacer el efecto de desplazamiento cuando se inicia el toque, luego eliminar el efecto de desplazamiento cuando el toque se mueve o termina.

Esto es lo que Apple tiene que decir sobre el manejo táctil en general, ya que mencionas al iPhone.

hacia adelante
fuente
¿El contenido del enlace sigue siendo relevante ahora?
Flavien Volken
1

Mi gusto personal es atribuir los :hoverestilos al :focusestado también, como:

p {
    color: red;
}

p:hover, p:focus {
    color: blue;
}

Luego con el siguiente HTML:

<p id="some-p-tag" tabindex="-1">WOOOO</p>

Y el siguiente JavaScript:

$("#some-p-tag").on("touchstart", function(e){
    e.preventDefault();
    var $elem = $(this);

    if($elem.is(":focus")) {
        //this can be registered as a "click" on a mobile device, as it's a double tap
        $elem.blur()
    }
    else {
        $elem.focus();
    }
});
pimbrouwers
fuente
1

Resuelto 2019 - Desplazarse al tacto

Ahora parece mejor evitar el uso de hover junto con ios o touch en general. El siguiente código aplica su css siempre que se mantenga el tacto y sin otros controles flotantes de ios. Hacer esto;

  1. Jquery agrega: $ ("p"). On ("touchstart", function (e) {$ (this) .focus (); e.preventDefault ();});

  2. CSS: reemplace p: hover con p: focus y agregue p: active

Opciones;

  • reemplace el selector jquery p con cualquier clase, etc.

  • para que el efecto permanezca, mantenga p: hover también, y agregue body {cursor: ponter;} para que un toque en cualquier lugar lo termine

  • pruebe los eventos de clic y mouseover, así como el inicio táctil en el mismo código (pero no probado)

  • eliminar e.preventDefault (); para permitir a los usuarios utilizar menús flotantes de iOS, por ejemplo, copiar

Notas

  • solo probado para elementos de texto, ios puede tratar las entradas, etc. de manera diferente

  • solo probado en iphone XR ios 12.1.12 y ipad 3 ios 9.3.5, usando Safari o Chrome.

darren
fuente
0

Una mezcla de Javascript nativo y jQuery:

var gFireEvent = function (oElem,sEvent) 
{
 try {
 if( typeof sEvent == 'string' && o.isDOM( oElem ))
 {
  var b = !!(document.createEvent),
     evt = b?document.createEvent("HTMLEvents"):document.createEventObject();
  if( b )    
  {  evt.initEvent(sEvent, true, true ); 
    return !oElem.dispatchEvent(evt);
  }
  return oElem.fireEvent('on'+sEvent,evt);
 }
 } catch(e) {}
 return false;
};


// Next you can do is (bIsMob etc you have to determine yourself):

   if( <<< bIsMob || bIsTab || bisTouch >>> )
   {
     $(document).on('mousedown', function(e)
     {
       gFireEvent(e.target,'mouseover' );
     }).on('mouseup', function(e)
     {
       gFireEvent(e.target,'mouseout' );
     });
   }
Codebeat
fuente
1
siento ser pedante pero jquery es javacript
matpol
2
@matpol: jQuery es javascript pero javascript no es jQuery. Especial para ti agrego la palabra 'puro', javascript puro. ¿Señor satisfecho?
Codebeat
no puede usar jquery sin usar javascript 'puro'. Solo lo digo porque algunas personas que frecuentan SO no parecen saberlo.
matpol
5
Una mezcla de Javascript nativo y jQuery, ¿Satisfecho?
Codebeat
0

La solución más fácil que encontré: tenía algunas etiquetas <span> con: hover css rules en ellas. Cambié por <a href = "javascript: void (0)"> y voilà. Los estilos de desplazamiento en iOS comenzaron a funcionar.

Becario Senior
fuente
0

El uso también puede usar CSS, agregar foco y activo (para IE7 y menos) al enlace oculto. Ejemplo de un menú ul dentro de un div con menú de clase:

.menu ul ul {display:none; position:absolute; left:100%; top:0; padding:0;}
.menu ul ul ul {display:none; position:absolute; top:0; left:100%;}
.menu ul ul a, .menu ul ul a:focus, .menu ul ul a:active { width:170px; padding:4px 4%; background:#e77776; color:#fff; margin-left:-15px; }
.menu ul li:hover > ul { display:block; }
.menu ul li ul li {margin:0;}

Es tarde y no ha sido probado, debería funcionar ;-)

Mark Groen
fuente