web-dev-qa-db-fra.com

Comment distinguer la souris "clic" et "glisser"

J'utilise jQuery.click pour gérer l'événement de clic de souris sur le graphe de Raphael, alors que j'ai besoin de gérer un événement de souris drag, le glissement de souris consiste en mousedown, mouseupet mousemove à Raphaël.

Il est difficile de distinguer click et drag parce que click contient également mousedown & mouseup, comment distinguer un clic de souris et un glissement de souris en Javascript?

146
Leem

Je pense que la différence est qu’il existe une mousemove entre mousedown et mouseup en un glissement, mais pas en un clic.

Vous pouvez faire quelque chose comme ça:

const element = document.createElement('div')
element.innerHTML = 'test'
document.body.appendChild(element)
let moved
let downListener = () => {
    moved = false
}
element.addEventListener('mousedown', downListener)
let moveListener = () => {
    moved = true
}
element.addEventListener('mousemove', moveListener)
let upListener = () => {
    if (moved) {
        console.log('moved')
    } else {
        console.log('not moved')
    }
}
element.addEventListener('mouseup', upListener)

// release memory
element.removeEventListener('mousedown', downListener)
element.removeEventListener('mousemove', moveListener)
element.removeEventListener('mouseup', upListener)
180
wong2

Si vous utilisez déjà jQuery:

var $body = $('body');
$body.on('mousedown', function (evt) {
  $body.on('mouseup mousemove', function handler(evt) {
    if (evt.type === 'mouseup') {
      // click
    } else {
      // drag
    }
    $body.off('mouseup mousemove', handler);
  });
});
33
Gustavo Rodrigues

Cela devrait bien fonctionner. Identique à la réponse acceptée (avec jQuery), mais le drapeau isDragging n'est réinitialisé que si la nouvelle position de la souris diffère de celle de l'événement mousedown. Contrairement à la réponse acceptée, cela fonctionne sur les versions récentes de Chrome, où mousemove est déclenché, que la souris ait été déplacée ou non.

var isDragging = false;
var startingPos = [];
$(".selector")
    .mousedown(function (evt) {
        isDragging = false;
        startingPos = [evt.pageX, evt.pageY];
    })
    .mousemove(function (evt) {
        if (!(evt.pageX === startingPos[0] && evt.pageY === startingPos[1])) {
            isDragging = true;
        }
    })
    .mouseup(function () {
        if (isDragging) {
            console.log("Drag");
        } else {
            console.log("Click");
        }
        isDragging = false;
        startingPos = [];
    });

Vous pouvez également ajuster le contrôle des coordonnées dans mousemove si vous souhaitez ajouter un peu de tolérance (c’est-à-dire traiter les petits mouvements comme des clics, pas comme des traînées).

17
nirvana-msu

Comme le souligne mrjrdnthms dans son commentaire sur la réponse acceptée, cela ne fonctionne plus sur Chrome (il déclenche toujours le déplacement de la souris), j'ai adapté la réponse de Gustavo (depuis que jQuery est utilisé) pour Chrome comportement.

var currentPos = [];

$(document).on('mousedown', function (evt) {

   currentPos = [evt.pageX, evt.pageY]

  $(document).on('mousemove', function handler(evt) {

    currentPos=[evt.pageX, evt.pageY];
    $(document).off('mousemove', handler);

  });

  $(document).on('mouseup', function handler(evt) {

    if([evt.pageX, evt.pageY].equals(currentPos))
      console.log("Click")
    else
      console.log("Drag")

    $(document).off('mouseup', handler);

  });

});

La fonction Array.prototype.equals provient de ceci réponse

12
Francisco Aquino

Si vous souhaitez utiliser Rxjs :

var element = document;

Rx.Observable
  .merge(
    Rx.Observable.fromEvent(element, 'mousedown').mapTo(0),
    Rx.Observable.fromEvent(element, 'mousemove').mapTo(1)
  )
  .sample(Rx.Observable.fromEvent(element, 'mouseup'))
  .subscribe(flag => {
      console.clear();
      console.log(flag ? "drag" : "click");
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://unpkg.com/@reactivex/[email protected]/dist/global/Rx.js"></script>

Ceci est un clone direct de ce que @ wong2 a fait dans sa réponse, mais converti en RxJs.

Également utilisation intéressante de sample . L'opérateur sample prendra la dernière valeur de la source (le merge de mousedown et mousemove) et l'émettra lorsque l'émetteur observable intérieur (mouseup) sera émis.

11
Dorus

Nettoyant ES2015

let drag = false;

document.addEventListener('mousedown', () => drag = false);
document.addEventListener('mousemove', () => drag = true);
document.addEventListener('mouseup', () => console.log(drag ? 'drag' : 'click'));

N'a rencontré aucun bogue, comme d'autres l'ont commenté.

10
Przemek

Utilisation de jQuery avec un minimum de 5 pixels x/y pour détecter le glissement:

var dragging = false;
$("body").on("mousedown", function(e) {
  var x = e.screenX;
  var y = e.screenY;
  dragging = false;
  $("body").on("mousemove", function(e) {
    if (Math.abs(x - e.screenX) > 5 || Math.abs(y - e.screenY) > 5) {
      dragging = true;
    }
  });
});
$("body").on("mouseup", function(e) {
  $("body").off("mousemove");
  console.log(dragging ? "drag" : "click");
});
5
silverwind

Si vous voulez filtrer la casse, procédez comme suit:

var moved = false;
$(selector)
  .mousedown(function() {moved = false;})
  .mousemove(function() {moved = true;})
  .mouseup(function(event) {
    if (!moved) {
        // clicked without moving mouse
    }
  });
3
jqgsninimo

Pure JS avec DeltaX et DeltaY

Cette DeltaX et DeltaY comme suggéré par un commentaire dans la réponse acceptée pour éviter l'expérience frustrante lorsque vous essayez de cliquer et d'obtenir une opération de glisser à la place en raison d'un déplacement de souris à un tick.

    deltaX = deltaY = 2;//px
    var element = document.getElementById('divID');
    element.addEventListener("mousedown", function(e){
        if (typeof InitPageX == 'undefined' && typeof InitPageY == 'undefined') {
            InitPageX = e.pageX;
            InitPageY = e.pageY;
        }

    }, false);
    element.addEventListener("mousemove", function(e){
        if (typeof InitPageX !== 'undefined' && typeof InitPageY !== 'undefined') {
            diffX = e.pageX - InitPageX;
            diffY = e.pageY - InitPageY;
            if (    (diffX > deltaX) || (diffX < -deltaX)
                    || 
                    (diffY > deltaY) || (diffY < -deltaY)   
                    ) {
                console.log("dragging");//dragging event or function goes here.
            }
            else {
                console.log("click");//click event or moving back in delta goes here.
            }
        }
    }, false);
    element.addEventListener("mouseup", function(){
        delete InitPageX;
        delete InitPageY;
    }, false);

   element.addEventListener("click", function(){
        console.log("click");
    }, false);
1
Waqas Bukhary

Pour une action publique sur une carte OSM (positionner un marqueur sur un clic), la question était la suivante: 1) comment déterminer la durée de l'activation/désactivation de la souris (vous ne pouvez pas imaginer créer un nouveau marqueur pour chaque clic) et 2) la souris se déplace pendant la descente vers le haut (l'utilisateur glisse la carte).

const map = document.getElementById('map');

map.addEventListener("mousedown", position); 
map.addEventListener("mouseup", calculate);

let posX, posY, endX, endY, t1, t2, action;

function position(e) {

  posX = e.clientX;
  posY = e.clientY;
  t1 = Date.now();

}

function calculate(e) {

  endX = e.clientX;
  endY = e.clientY;
  t2 = (Date.now()-t1)/1000;
  action = 'inactive';

  if( t2 > 0.5 && t2 < 1.5) { // Fixing duration of mouse down->up

      if( Math.abs( posX-endX ) < 5 && Math.abs( posY-endY ) < 5 ) { // 5px error on mouse pos while clicking
         action = 'active';
         // --------> Do something
      }
  }
  console.log('Down = '+posX + ', ' + posY+'\nUp = '+endX + ', ' + endY+ '\nAction = '+ action);    

}
1
Wolden