So unterscheiden Sie Mausklick und Ziehen

165

Ich verwende jQuery.clickdas Mausklickereignis im Raphael-Diagramm. In der Zwischenzeit muss ich das Mausereignis behandeln drag, aus dem das Ziehen der Maus besteht mousedown, mouseupund mousemovein Raphael.

Es ist schwer zu unterscheiden clickund dragweil clickauch mousedown& enthalten mouseup, wie kann ich Mausklick & Mauszug dann in Javascript unterscheiden?

Leem
quelle

Antworten:

192

Ich denke, der Unterschied ist, dass es ein mousemoveZwischen- mousedownund mouseupein Ziehen gibt, aber nicht ein Klicken.

Sie können so etwas tun:

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)
wong2
quelle
38
Denken Sie daran, ein Minimum Delta X oder Y für die Mausbewegung zu benötigen, um einen Widerstand auszulösen. Es wäre frustrierend zu versuchen, zu klicken und stattdessen eine Drag-Operation zu erhalten, da sich ein Mauszeiger mit einem Tick bewegt
Erik Rydgren,
12
Ich glaube nicht, dass dies im letzten Chrom mehr funktioniert: 32.0.1700.72 Mousemove wird ausgelöst, ob Sie die Maus bewegen oder nicht
mrjrdnthms
17
Dieser akzeptierte Antwortcode sollte eine minimale Delta-Bedingung zwischen den XY-Mauskoordinaten bei mousedownund mouseupanstelle des Abhörens des mousemoveEreignisses zum Setzen eines Flags enthalten. Darüber hinaus würde es das von @mrjrdnthms
Billybobbonnet
2
Ich verwende Chrome 56.0.2924.87 (64-Bit) und habe keine Probleme mit den von @mrjrdnthms beschriebenen Problemen.
jkupczak
1
@AmerllicA dies ist wahrscheinlich kein Fehler, aber erwartetes Verhalten, aber Sie könnten die Mouseenter- und Mouseleave-Ereignisse beobachten, wenn dies für Ihren Anwendungsfall interessant ist
Rivenfall
37

Falls Sie jQuery bereits verwenden:

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);
  });
});
Gustavo Rodrigues
quelle
Selbst wenn Sie die Maus beim Klicken ein wenig bewegen, wird dies angezeigt drag. Ein zusätzlicher Bereich, wie andere Kommentare sagen, kann hier erforderlich sein.
ChiMo
@ChiMo Was ich verwendet habe, ist das Speichern der Mausposition von der ersten evtund das Vergleichen mit der Position der zweiten evt, also zum Beispiel : if (evt.type === 'mouseup' || Math.abs(evt1.pageX - evt2.pageX) < 5 || Math.abs(evt1.pageY - evt2.pageY) < 5) { ....
Gustavo Rodrigues
1
Ich habe alle anderen Antworten auf diese Frage ausprobiert, und dies ist die einzige, die beim Suchen funktioniert hat .on('mouseup mousemove touchend touchmove'), und außerdem werden keine Positionsvariablen festgelegt. Tolle Lösung!
TheThirdMan
Manchmal, wenn ich auf ein Element geklickt habe, gibt "evt.type" stattdessen "mousemove" zurück, stattdessen mit der Maus. Wie kann ich dieses Problem beheben?
Libu Mathew
27

Reiniger ES2015

let drag = false;

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

Ich habe keine Fehler festgestellt, wie andere kommentieren.

Przemek
quelle
6
Dies leidet unter Klicks mit winzigen Bewegungen.
Amir Keibi
1
@AmirKeibi Sie könnten die Anzahl der Mausbewegungen zählen (oder sogar die Entfernung zwischen den beiden Klicks berechnen, aber das wäre übertrieben)
Rivenfall
19

Das sollte gut funktionieren. Ähnlich wie die akzeptierte Antwort (obwohl mit jQuery), aber das isDraggingFlag wird nur zurückgesetzt, wenn sich die neue Mausposition von der bei mousedownEreignis unterscheidet. Im Gegensatz zur akzeptierten Antwort funktioniert dies bei neueren Chrome-Versionen, bei denen mousemoveunabhängig davon, ob die Maus bewegt wurde oder nicht , eine Abgabe erfolgt.

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 = [];
    });

Sie können auch das Einchecken der Koordinaten anpassen, mousemovewenn Sie ein wenig Toleranz hinzufügen möchten (dh winzige Bewegungen als Klicks behandeln, nicht als Schleppen).

nirvana-msu
quelle
12

Wenn Sie Rxjs verwenden möchten :

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>

Dies ist ein direkter Klon dessen, was @ wong2 in seiner Antwort getan hat, aber in RxJs konvertiert.

Auch interessante Verwendung von sample. Der sampleOperator nimmt den neuesten Wert aus der Quelle ( mergevon mousedownund mousemove) und gibt ihn aus, wenn das innere Observable ( mouseup) ausgegeben wird.

Dorus
quelle
22
Ich schreibe meinen gesamten Code mit Observablen, damit mein Chef niemanden einstellen kann, der mich ersetzt.
Reactgular
11

Wie mrjrdnthms in seinem Kommentar zur akzeptierten Antwort hervorhebt, funktioniert dies in Chrome nicht mehr (es wird immer die Mausbewegung ausgelöst). Ich habe Gustavos Antwort (da ich jQuery verwende) angepasst, um das Chrome-Verhalten zu beheben.

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);

  });

});

Die Array.prototype.equalsFunktion ergibt sich aus dieser Antwort

Francisco Aquino
quelle
1
Das hat bei mir fast funktioniert, aber ich habe einen Fehler vom [evt.pageX, evt.pageY].equals()Befehl erhalten. Ich habe das durch ersetzt (evt.pageX === currentPos[0] && evt.pageY===currentPos[1])und alles war gut. :)
user2441511
Der equalsCode muss über den Link am Ende meines Beitrags hinzugefügt werden
Francisco Aquino
Ah, das erklärt es. Vielen Dank.
user2441511
1
Ich kann die Logik nicht verstehen. Warum aktualisieren Sie currentPosauf mousemove? Bedeutet das nicht, dass Sie einige Drags als Klicks behandeln würden?
Nirvana-Msu
1
Dies wird nicht ausgelöst, wenn Sie "mouseup"die Maus noch bewegen.
ChiMo
9

Alle diese Lösungen brechen entweder bei winzigen Mausbewegungen oder sind zu kompliziert.

Hier ist eine einfache anpassbare Lösung mit zwei Ereignis-Listenern. Delta ist der Abstand in Pixel, den Sie horizontal oder vertikal zwischen den Aufwärts- und Abwärtsereignissen verschieben müssen, damit der Code ihn als Ziehen und nicht als Klicken klassifiziert. Dies liegt daran, dass Sie manchmal die Maus oder Ihren Finger einige Pixel bewegen, bevor Sie sie anheben.

const delta = 6;
let startX;
let startY;

element.addEventListener('mousedown', function (event) {
  startX = event.pageX;
  startY = event.pageY;
});

element.addEventListener('mouseup', function (event) {
  const diffX = Math.abs(event.pageX - startX);
  const diffY = Math.abs(event.pageY - startY);

  if (diffX < delta && diffY < delta) {
    // Click!
  }
});
andreyrd
quelle
Mit Abstand die beste Antwort!
Giorgio Tempesta
Hallo @andreyrd, darf ich wissen, was deltadafür verwendet wird? hat es etwas mit tippen auf mobilen gerät zu tun?
Haziq
5

Verwenden von jQuery mit einem 5-Pixel-x / y-Schwellenwert zum Erkennen des Widerstands:

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");
});
Silberwind
quelle
2

Wenn Sie nur den Drag-Fall herausfiltern möchten, gehen Sie folgendermaßen vor:

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

Pure JS mit DeltaX und DeltaY

Dies DeltaX und DeltaY, wie durch einen Kommentar in der akzeptierten Antwort vorgeschlagen, um die frustrierende Erfahrung zu vermeiden, wenn versucht wird, zu klicken und stattdessen eine Ziehoperation aufgrund einer Mausbewegung mit einem Tick auszuführen.

    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);
Waqas Bukhary
quelle
1

Für eine öffentliche Aktion auf einer OSM-Karte (Positionieren eines Markers beim Klicken) lautete die Frage: 1) Wie wird die Dauer der Maus nach unten-> nach oben bestimmt (Sie können sich nicht vorstellen, für jeden Klick einen neuen Marker zu erstellen) und 2) Die Maus bewegt sich während des Abwärts-> Aufwärtsfahrens (dh der Benutzer zieht die Karte).

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);    

}
Wolden
quelle
0

Eine andere Lösung für klassenbasiertes Vanille-JS unter Verwendung eines Abstandsschwellenwerts

private initDetectDrag(element) {
    let clickOrigin = { x: 0, y: 0 };
    const dragDistanceThreshhold = 20;

    element.addEventListener('mousedown', (event) => {
        this.isDragged = false
        clickOrigin = { x: event.clientX, y: event.clientY };
    });
    element.addEventListener('mousemove', (event) => {
        if (Math.sqrt(Math.pow(clickOrigin.y - event.clientY, 2) + Math.pow(clickOrigin.x - event.clientX, 2)) > dragDistanceThreshhold) {
            this.isDragged = true
        }
    });
}

Und zur Klasse hinzufügen (SOMESLIDER_ELEMENT kann auch ein globales Dokument sein):

private isDragged: boolean;
constructor() {
    this.initDetectDrag(SOMESLIDER_ELEMENT);
    this.doSomeSlideStuff(SOMESLIDER_ELEMENT);
    element.addEventListener('click', (event) => {
        if (!this.sliderIsDragged) {
            console.log('was clicked');
        } else {
            console.log('was dragged, ignore click or handle this');
        }
    }, false);
}
Tim Rasim
quelle
0

Wenn Sie das Klick- oder Ziehverhalten eines bestimmten Elements überprüfen möchten, können Sie dies tun, ohne auf den Körper hören zu müssen.

$(document).ready(function(){
  let click;
  
  $('.owl-carousel').owlCarousel({
    items: 1
  });
  
  // prevent clicks when sliding
  $('.btn')
    .on('mousemove', function(){
      click = false;
    })
    .on('mousedown', function(){
      click = true;
    });
    
  // change mouseup listener to '.content' to listen to a wider area. (mouse drag release could happen out of the '.btn' which we have not listent to). Note that the click will trigger if '.btn' mousedown event is triggered above
  $('.btn').on('mouseup', function(){
    if(click){
      $('.result').text('clicked');
    } else {
      $('.result').text('dragged');
    }
  });
});
.content{
  position: relative;
  width: 500px;
  height: 400px;
  background: #f2f2f2;
}
.slider, .result{
  position: relative;
  width: 400px;
}
.slider{
  height: 200px;
  margin: 0 auto;
  top: 30px;
}
.btn{
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  height: 100px;
  background: #c66;
}
.result{
  height: 30px;
  top: 10px;
  text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/owl.carousel.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.carousel.min.css" />
<div class="content">
  <div class="slider">
    <div class="owl-carousel owl-theme">
      <div class="item">
        <a href="#" class="btn" draggable="true">click me without moving the mouse</a>
      </div>
      <div class="item">
        <a href="#" class="btn" draggable="true">click me without moving the mouse</a>
      </div>
    </div>
    <div class="result"></div>
  </div>
  
</div>

Lasithds
quelle
0

aus der Antwort von @Przemek,

function listenClickOnly(element, callback, threshold=10) {
  let drag = 0;
  element.addEventListener('mousedown', () => drag = 0);
  element.addEventListener('mousemove', () => drag++);
  element.addEventListener('mouseup', e => {
    if (drag<threshold) callback(e);
  });
}

listenClickOnly(
  document,
  () => console.log('click'),
  10
);

Jehong Ahn
quelle