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?

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)

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

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.


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


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/rxjs@5.4.1/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.


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


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

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

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

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

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

}

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

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>


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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.