web-dev-qa-db-fra.com

éviter le touchstart lors du balayage

J'ai une liste déroulante sur un appareil mobile. Ils veulent que les gens puissent faire défiler la liste en balayant et aussi sélectionner une ligne en appuyant sur. 

La prise combine les deux. Je ne veux pas qu'une ligne soit sélectionnée si vous faites défiler la liste. Voici ce que j'ai trouvé:

Ne se déclenche pas lors du défilement: 

  • cliquez sur
  • mouseup 

Ne se déclenche lors du défilement:

  • souris vers le bas 
  • touchstart 
  • toucher 

La solution simple consiste à s'en tenir à l'événement click. Mais ce que nous constatons, c’est que sur certains appareils BlackBerry, il existe un décalage TRÈS perceptible entre le touchstart et le déclenchement par un clic ou une souris. Ce délai est suffisamment important pour le rendre inutilisable sur ces appareils.

Cela nous laisse donc avec les autres options. Cependant, avec ces options, vous pouvez faire défiler la liste sans déclencher la ligne sur laquelle vous avez appuyé pour démarrer le défilement.

Quelle est la meilleure pratique ici pour résoudre ce problème? 

35
DA.
var touchmoved;
$('button').on('touchend', function(e){
    if(touchmoved != true){
        // button click action
    }
}).on('touchmove', function(e){
    touchmoved = true;
}).on('touchstart', function(){
    touchmoved = false;
});
40
Levarne Sobotker

Ce que vous voulez fondamentalement faire est de détecter ce qu'est un balayage et ce qu'est un clic.

Nous pouvons fixer certaines conditions:

  1. Balayer, c'est quand vous touchez au point p1, puis déplacez votre doigt sur le point p2 tout en maintenant le doigt sur l'écran, puis relâchez le doigt.
  2. Un clic signifie que vous appuyez sur Démarrer, puis sur le même élément.

Ainsi, si vous enregistrez les coordonnées de l'endroit où votre touchStart s'est produite, vous pouvez mesurer la différence à touchEnd. Si le changement est suffisamment important, considérez-le comme un balayage, sinon, considérez-le comme un clic.

En outre, si vous voulez le faire vraiment bien, vous pouvez également détecter l'élément que vous "survolez" avec votre doigt pendant une touchMove, et si vous n'êtes pas encore à l'élément sur lequel vous avez commencé le clic, vous pouvez une méthode clickCancel qui supprime les surbrillances, etc.

// grab an element which you can click just as an example
var clickable = document.getElementById("clickableItem"),
// set up some variables that we need for later
currentElement,
clickedElement;

// set up touchStart event handler
var onTouchStart = function(e) {
    // store which element we're currently clicking on
    clickedElement = this;
    // listen to when the user moves finger
    this.addEventListener("touchMove" onTouchMove);
    // add listener to when touch end occurs
    this.addEventListener("touchEnd", onTouchEnd);
};
// when the user swipes, update element positions to swipe
var onTouchMove = function(e) {
    // ... do your scrolling here

    // store current element
    currentElement = document.elementFromPoint(x, y);
    // if the current element is no longer the same as we clicked from the beginning, remove highlight
    if(clickedElement !== currentElement) {
        removeHighlight(clickedElement);
    }
};
// this is what is executed when the user stops the movement
var onTouchEnd = function(e) {
    if(clickedElement === currentElement) {
        removeHighlight(clickedElement);
        // .... execute click action
    }

    // clean up event listeners
    this.removeEventListener("touchMove" onTouchMove);
    this.removeEventListener("touchEnd", onTouchEnd);
};
function addHighlight(element) {
    element.className = "highlighted";
}
function removeHighlight(element) {
    element.className = "";
}
clickable.addEventListener("touchStart", onTouchStart);

Ensuite, vous devrez également ajouter des écouteurs à votre élément déroulant, mais vous ne devrez pas vous inquiéter de ce qui se passera si le doigt s'est déplacé entre touchStart et touchEnd.

var scrollable = document.getElementById("scrollableItem");

// set up touchStart event handler
var onTouchStartScrollable = function(e) {
    // listen to when the user moves finger
    this.addEventListener("touchMove" onTouchMoveScrollable);
    // add listener to when touch end occurs
    this.addEventListener("touchEnd", onTouchEndScrollable);
};
// when the user swipes, update element positions to swipe
var onTouchMoveScrollable = function(e) {
    // ... do your scrolling here
};
// this is what is executed when the user stops the movement
var onTouchEndScrollable = function(e) {
    // clean up event listeners
    this.removeEventListener("touchMove" onTouchMoveScrollable);
    this.removeEventListener("touchEnd", onTouchEndScrollable);
};
scrollable.addEventListener("touchStart", onTouchStartScrollable);

// Simon A.

22

Voici ce que j’ai finalement trouvé pour permettre à une liste d’éléments de défiler par balayage, mais également de «déclencher» chaque élément d’un robinet. De plus, vous pouvez toujours utiliser un clavier (en utilisant onclick). 

Je pense que cela ressemble à la réponse de Netlight_Digital_Media. J'ai besoin d'étudier celle-là un peu plus. 

$(document)
// log the position of the touchstart interaction
.bind('touchstart', function(e){ 
  touchStartPos = $(window).scrollTop();
})
// log the position of the touchend interaction
.bind('touchend', function(e){
  // calculate how far the page has moved between
  // touchstart and end. 
  var distance = touchStartPos - $(window).scrollTop();

  var $clickableItem; // the item I want to be clickable if it's NOT a swipe

  // adding this class for devices that
  // will trigger a click event after
  // the touchend event finishes. This 
  // tells the click event that we've 
  // already done things so don't repeat

  $clickableItem.addClass("touched");      

  if (distance > 20 || distance < -20){
        // the distance was more than 20px
        // so we're assuming they intended
        // to swipe to scroll the list and
        // not selecting a row. 
    } else {
        // we'll assume it was a tap 
        whateverFunctionYouWantToTriggerOnTapOrClick()
    }
});


$($clickableItem).live('click',function(e){
 // for any non-touch device, we need 
 // to still apply a click event
 // but we'll first check to see
 // if there was a previous touch
 // event by checking for the class
 // that was left by the touch event.
if ($(this).hasClass("touched")){
  // this item's event was already triggered via touch
  // so we won't call the function and reset this for
  // the next touch by removing the class
  $(this).removeClass("touched");
} else {
  // there wasn't a touch event. We're
  // instead using a mouse or keyboard
  whateverFunctionYouWantToTriggerOnTapOrClick()
}
});
17
DA.

Citant de DA .:

Ceci est un exemple de travail:

var touch_pos;
$(document).on('touchstart', '.action-feature', function(e) {
  e.preventDefault();
  touch_pos = $(window).scrollTop();
}).on('click touchend', '.action-feature', function(e) {
  e.preventDefault();
  if(e.type=='touchend' && (Math.abs(touch_pos-$(window).scrollTop())>3)) return;
  alert("only accessed when it's a click or not a swipe");
});
6
josualeonard

Certaines de ces solutions ont fonctionné pour moi, mais au final, j'ai constaté que cette bibliothèque légère était plus simple à configurer.

Tocca.js: https://github.com/GianlucaGuarini/Tocca.js

Il est assez flexible et détecte le toucher, les glissements, les tapotements, etc.

3
Patrice Wrex

J'ai eu le même problème, voici une solution rapide qui fonctionne pour moi

$(document).on('touchstart', 'button', function(evt){ 
    var oldScrollTop = $(window).scrollTop();
    window.setTimeout( function() {
        var newScrollTop = $(window).scrollTop();
        if (Math.abs(oldScrollTop-newScrollTop)<3) $button.addClass('touchactive');
    }, 200);
});

fondamentalement, au lieu de manipuler touchstart immédiatement, attendez quelques millisecondes (200 ms dans cet exemple), puis vérifiez la position de défilement, si la position de défilement a été modifiée, nous n'avons pas besoin de gérer touchstart.

2
Mahes

Je suis tombé sur cette solution élégante qui fonctionne comme un charme avec jQuery. Mon problème empêchait les éléments de la liste d'appeler leur événement tactile lors du défilement. Cela devrait également fonctionner pour glisser.

  1. liez touchstart à chaque élément qui sera défilé ou balayé à l'aide d'une classe 'listObject'

    $('.listObject').live('touchstart', touchScroll);
    
  2. Puis, à chaque élément, affectez un objet de données attr définissant la fonction à appeler.

    <button class='listObject' data-object=alert('You are alerted !')>Alert Me</button>
    

La fonction suivante fera la différence entre un tapotement et un défilement ou un balayage.

function touchScroll(e){

    var objTarget = $(event.target);

    if(objTarget.attr('data-object')){
        var fn = objTarget.attr('data-object'); //function to call if tapped    
    }   

    if(!touchEnabled){// default if not touch device
        eval(fn);
        console.log("clicked", 1);
        return;
    }

    $(e.target).on('touchend', function(e){
        eval(fn); //trigger the function
        console.log("touchEnd")      
        $(e.target).off('touchend');
    });

    $(e.target).on('touchmove', function(e){
        $(e.target).off('touchend');
        console.log("moved")
    }); 

}
1
alQemist

si vous voulez le faire pour plusieurs éléments et que vous avez également besoin d'événements de souris et de pointeur

var elems = $('YOURMULTISELECTOR'); // selector for multiple elements
elems.unbind('mousdown pointerdown touchstart touchmove mouseup pointerup touchend');
var elem = null;
elems.on('mousdown pointerdown touchstart', function (e) {
    elem = yourSingleSelector(e);
}).on('touchmove', function (e) {
    elem = null;                
}).on('mouseup pointerup touchend', function (e) { 
    if (elem == yourSingleSelector(e)) {                    
        // do something
    }
});
0
A.J.Bauer

jQuery Mobile a un événement .tap() qui semble avoir le comportement que vous attendez:

L'événement tap jQuery Mobile se déclenche après un événement tactile rapide et complet qui se produit sur un seul objet cible. C'est l'équivalent gestuel d'un événement de clic standard qui est déclenché sur l'état de libération du geste tactile.

Cela pourrait ne pas nécessairement répondre à la question, mais pourrait être une alternative utile à certaines.

0
Wex

J'utilise ce bout de code pour que les boutons ne soient déclenchés (au bout du doigt) que s'ils ne sont pas glissés:

var startY;
var yDistance;

function touchHandler(event) {
    touch = event.changedTouches[0];
    event.preventDefault();
}

$('.button').on("touchstart", touchHandler, true);
$('.button').on("touchmove", touchHandler, true);

$('.button').on("touchstart", function(){
    startY = touch.clientY;
});

$('.button').on('touchend', function(){

    yDistance = startY - touch.clientY;

    if(Math.abs(yDist) < 30){

        //button response here, only if user is not swiping
        console.log("button pressed")
    }
});
0
LocDog

Je l'ai fait avec un peu de travail différent. Ce n'est certainement pas très élégant et certainement pas adapté à la plupart des situations, mais cela a fonctionné pour moi. 

J'utilise toggleSlide () de jQuery pour ouvrir et fermer les divs d'entrée, déclenchant la diapositive au toucher. Le problème était que lorsque l'utilisateur voulait faire défiler, le div touché s'ouvrait. Pour éviter que cela ne se produise (ou l'inverser avant que l'utilisateur ne le remarque), j'ai ajouté un événement touchslide au document qui fermerait le dernier div touché. 

Plus en profondeur, voici un extrait de code:

var lastTouched;

document.addEventListener('touchmove',function(){
    lastTouched.hide();
});

$('#button').addEventListener('touchstart',function(){
    $('#slide').slideToggle();
    lastTouched = $('#slide');
});

La variable globale stocke le dernier div touché et si l'utilisateur glisse, l'événement document.touchmove le cache. Parfois, une div commence à pointer, mais cela fonctionne pour ce dont j'ai besoin et est assez simple pour que je puisse le faire.

0
Abraham Brookes