Cara membedakan "klik" dan "seret" mouse

165

Saya menggunakan jQuery.clickuntuk menangani acara klik mouse pada grafik Raphael, sementara itu, saya perlu menangani dragacara mouse, terdiri dari drag mouse mousedown, mouseupdan mousemovedalam Raphael.

Sulit dibedakan clickdan dragkarena clickjuga mengandung mousedown& mouseup, Bagaimana saya bisa membedakan "klik" & tarik "mouse" kemudian di Javascript?

Leem
sumber

Jawaban:

192

Saya pikir perbedaannya adalah bahwa ada di mousemoveantara mousedowndan mouseupdi tarik, tetapi tidak di klik.

Anda dapat melakukan sesuatu seperti ini:

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
sumber
38
Ingatlah untuk meminta delta X atau Y minimum pada mouse untuk memicu hambatan. Akan frustasi untuk mencoba mengklik dan mendapatkan operasi seret sebagai gantinya karena gerakan mouse satu tik
Erik Rydgren
12
Saya tidak berpikir ini bekerja lagi di chrome terbaru: 32.0.1700.72 Mousemove menyalakan apakah Anda menggerakkan mouse atau tidak
mrjrdnthms
17
Kode jawaban yang diterima ini harus mencakup kondisi delta minimum antara koordinat XY mouse di mousedowndan mouseupalih-alih mendengarkan mousemoveacara untuk mengatur bendera. Selain itu, itu akan memperbaiki masalah yang disebutkan oleh @mrjrdnthms
Billybobbonnet
2
Saya menjalankan Chrome 56.0.2924.87 (64-bit) dan saya tidak mengalami masalah yang dijelaskan oleh @rjrdnthms.
jkupczak
1
@AmerllicA ini mungkin bukan bug tetapi perilaku yang diharapkan, namun Anda dapat menonton acara mouseenter dan mouseleave jika itu menarik untuk kasus penggunaan Anda
Rivenfall
37

Jika Anda sudah menggunakan 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);
  });
});
Gustavo Rodrigues
sumber
Bahkan jika Anda menggerakkan mouse sedikit saat mengklik, ini akan mengatakan drag. Ruang lingkup tambahan seperti komentar lain mungkin diperlukan di sini.
ChiMo
@ChiMo Yang saya telah menggunakan adalah menyimpan posisi mouse dari pertama evtdan membandingkan dengan posisi kedua evt, sehingga, misalnya: if (evt.type === 'mouseup' || Math.abs(evt1.pageX - evt2.pageX) < 5 || Math.abs(evt1.pageY - evt2.pageY) < 5) { ....
Gustavo Rodrigues
1
Saya mencoba semua jawaban lain untuk pertanyaan ini, dan ini adalah satu-satunya yang berfungsi ketika memeriksa .on('mouseup mousemove touchend touchmove'), dan di atas itu tidak menetapkan variabel posisi. Solusi bagus!
TheThirdMan
Kadang-kadang ketika saya mengklik elemen "evt.type" return "mousemove" sebagai ganti mouseup. Bagaimana saya bisa mengatasi masalah itu?
Libu Mathew
27

Pembersih ES2015

let drag = false;

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

Tidak mengalami bug, seperti komentar orang lain.

Przemek
sumber
6
Ini menderita dari klik dengan gerakan kecil.
Amir Keibi
1
@AmirKeibi Anda dapat menghitung jumlah mousemoves (atau bahkan menghitung jarak antara dua klik tapi itu akan berlebihan)
Rivenfall
19

Ini harus bekerja dengan baik. Mirip dengan jawaban yang diterima (meskipun menggunakan jQuery), tetapi isDraggingflag hanya direset jika posisi mouse baru berbeda dari yang di mousedownacara. Berbeda dengan jawaban yang diterima, itu bekerja pada versi terbaru Chrome, di mana mousemovedipecat terlepas dari apakah mouse dipindahkan atau tidak.

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

Anda juga dapat menyesuaikan check-in koordinat mousemovejika Anda ingin menambahkan sedikit toleransi (mis. Memperlakukan gerakan kecil sebagai klik, bukan menyeret).

nirvana-msu
sumber
12

Jika Anda ingin menggunakan 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>

Ini adalah tiruan langsung dari apa yang dilakukan @ wong2 dalam jawabannya, tetapi dikonversi ke RxJs.

Juga penggunaan menarik sample. The sampleOperator akan mengambil nilai terbaru dari sumber (yang mergedari mousedowndan mousemove) dan memancarkan ketika batin diamati ( mouseup) memancarkan.

Dorus
sumber
22
Saya menulis semua kode saya dengan dapat diamati sehingga bos saya tidak dapat mempekerjakan orang lain untuk menggantikan saya.
Reactgular
11

Seperti yang ditunjukkan oleh mrjrdnthms dalam komentarnya tentang jawaban yang diterima, ini tidak lagi berfungsi pada Chrome (ini selalu memicu mousemove), saya telah mengadaptasi jawaban Gustavo (karena saya menggunakan jQuery) untuk mengatasi perilaku Chrome.

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

  });

});

The Array.prototype.equalsfungsi berasal dari ini jawabannya

Francisco Aquino
sumber
1
Ini hampir berhasil untuk saya, tetapi saya mendapat kesalahan dari [evt.pageX, evt.pageY].equals()perintah. Saya menggantinya dengan (evt.pageX === currentPos[0] && evt.pageY===currentPos[1]), dan semuanya baik-baik saja. :)
user2441511
The equalskebutuhan kode yang akan ditambahkan dari link di bagian bawah posting saya
Francisco Aquino
Ah, itu menjelaskannya. Terima kasih.
user2441511
1
Sepertinya saya tidak mengerti logika. Mengapa Anda memperbarui currentPospada mousemove? Bukankah ini berarti bahwa Anda akan memperlakukan beberapa hambatan sebagai klik?
nirvana-msu
1
Ini tidak menyala jika Anda "mouseup"masih menggerakkan mouse.
ChiMo
9

Semua solusi ini bisa pecah pada pergerakan mouse kecil, atau terlalu rumit.

Berikut ini adalah solusi mudah beradaptasi yang menggunakan dua pendengar acara. Delta adalah jarak dalam piksel yang harus Anda pindahkan secara horizontal atau vertikal antara peristiwa atas dan bawah untuk mengklasifikasikan kode sebagai seret alih-alih klik. Ini karena kadang-kadang Anda akan menggerakkan mouse atau jari Anda beberapa piksel sebelum mengangkatnya.

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
sumber
Sejauh ini jawaban terbaik!
Giorgio Tempesta
Hai @ andreyrd, bolehkah saya tahu apa deltayang digunakan untuk ini? itu ada hubungannya dengan keran di perangkat seluler?
Haziq
1
@Haziq Saya pikir sebagai orang yang disebutkan dalam komentar dari solusi teratas deltadigunakan untuk "Akan membuat frustasi untuk mencoba mengklik dan mendapatkan operasi drag bukan karena mouse satu tik"
Michael Bykhovtsev
1
Saya memperbarui jawabannya dengan penjelasan. Pada dasarnya jika jari Anda kurang dari 6 piksel, itu masih akan dihitung sebagai klik. Jika bergerak 6 atau lebih piksel, itu akan dihitung sebagai hambatan.
andreyrd
5

Menggunakan jQuery dengan 5 x x / y theshold untuk mendeteksi hambatan:

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");
});
angin perak
sumber
2

Jika hanya untuk menyaring kotak tarik, lakukan seperti ini:

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

JS murni dengan DeltaX dan DeltaY

DeltaX dan DeltaY ini seperti yang disarankan oleh komentar dalam jawaban yang diterima untuk menghindari pengalaman frustasi ketika mencoba mengklik dan mendapatkan operasi seret bukan karena gerakan satu centang.

    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
sumber
1

Untuk tindakan publik pada peta OSM (posisikan penanda pada klik) pertanyaannya adalah: 1) bagaimana menentukan durasi mouse turun -> atas (Anda tidak dapat membayangkan membuat penanda baru untuk setiap klik) dan 2) melakukan mouse bergerak selama down-> atas (yaitu pengguna menyeret peta).

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
sumber
0

Solusi lain menggunakan untuk vanilla JS berbasis kelas menggunakan jarak ambang batas

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

Dan tambahkan ke kelas (SOMESLIDER_ELEMENT juga dapat menjadi dokumen global):

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
sumber
0

Jika Anda ingin memeriksa perilaku klik atau seret elemen tertentu, Anda dapat melakukan ini tanpa harus mendengarkan isi.

$(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
sumber
0

dari jawaban @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
sumber