Fare “tıklama” ve “sürükleme” nasıl ayırt edilir


165

Kullandığım jQuery.clickRaphael grafik üzerinde fare click olayına, bu arada, ben fare işlemek için gereken dragolay, fare sürükleme oluşur mousedown, mouseupve mousemoveRaphael'de.

Ayırt etmek zordur clickve dragçünkü clickaynı zamanda ihtiva mousedown& mouseup, nasıl JavaScript ardından "sürükle" & fare "klik" fare ayırt edebilirsiniz?

Yanıtlar:


192

Aradaki fark, sürükleme mousemovearasında mousedownve mouseupsürüklemenin var olması, ancak bir tıklamanın olmamasıdır.

Bunun gibi bir şey yapabilirsiniz:

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
Sürüklemeyi tetiklemek için fare tekerleğinde minimum delta X veya Y gerektiğini unutmayın. Tek bir kene mousemove nedeniyle tıklamak ve sürükleme işlemi almak için sinir bozucu olurdu
Erik Rydgren 18:11

12
Bunun artık en son kromda çalıştığını düşünmüyorum: 32.0.1700.72 Mousemove, fareyi hareket ettirseniz de oynamasanız da ateş ediyor
mrjrdnthms

17
Kabul edilen bu cevap kodu, bir bayrak ayarlamak için olayı dinlemek yerine mousedownve XY fare koordinatları arasında minimum delta koşulu içermelidir . Dahası, mrjrdnthmsmouseupmousemove
Billybobbonnet

2
Chrome 56.0.2924.87 (64 bit) çalıştırıyorum ve @mrjrdnthms'ın açıkladığı sorunları yaşamıyorum.
jkupczak

1
Bu muhtemelen bir hata değil, beklenen davranış değil, ancak kullanım durumunuz için
ilginçse mouseenter ve mouseleave

37

Zaten jQuery kullanıyorsanız:

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

Tıklarken fareyi biraz hareket ettirseniz bile bu söylenecektir drag. Burada, diğer yorumların söylediği gibi ekstra bir kapsamın gerekli olabileceği belirtilmiştir.
ChiMo

@ChiMo Ne kullanıyorum ediyorum birinciden fare konumunu saklamak olduğunu evtve ikinci pozisyonuyla karşılaştırarak evtörneğin, bu yüzden,: if (evt.type === 'mouseup' || Math.abs(evt1.pageX - evt2.pageX) < 5 || Math.abs(evt1.pageY - evt2.pageY) < 5) { ....
Gustavo Rodrigues

1
Bu sorunun tüm diğer cevaplarını denedim ve bu kontrol ederken işe yarayan tek şeydi .on('mouseup mousemove touchend touchmove')ve bunun üzerine konum değişkenleri ayarlanmadı. Harika bir çözüm!
TheThirdMan

Bazen bir öğeyi tıklattığımda farenin yerine "evt.type" döndürür Bu sorunu nasıl çözebilirim?
Libu Mathew

27

Temizleyici ES2015

let drag = false;

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

Diğerleri yorum gibi herhangi bir hata ile karşılaşmadım.


6
Bu, küçük hareketlerle yapılan tıklamalardan muzdariptir.
Amir Keibi

1
@AmirKeibi mousemoves sayısını sayabilir (hatta iki tıklama arasındaki mesafeyi hesaplayabilirsiniz, ancak bu aşırıya
kaçabilir

19

Bu iyi olmalı. Kabul edilen cevaba benzer (jQuery kullanılmasına rağmen), ancak isDraggingbayrak yalnızca yeni fare konumu mousedownolaydaki durumdan farklıysa sıfırlanır . Kabul edilen cevabın aksine, Chrome'un son sürümlerinde çalışır, burada mousemovefarenin hareket ettirilip değiştirilmediğine bakılmaksızın tetiklenir.

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

mousemoveBiraz tolerans eklemek istiyorsanız koordinat kontrolünü de ayarlayabilirsiniz (örn. Küçük hareketleri sürükleme olarak değil tıklama olarak kabul edin).


12

Rxjs kullanmak istiyorsanız :

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>

Bu, @ wong2'nin cevabında yaptıklarının doğrudan bir klonudur, ancak RxJ'lere dönüştürülmüştür.

Ayrıca ilginç kullanımı sample. sampleOperatör kaynağından en son değeri alır (edecek mergeait mousedownve mousemove) ve ne zaman iç gözlenebilir (o yayarlar mouseup) yayar.


22
Tüm kodumu gözlemlenebilirlerle yazıyorum, böylece patronum beni değiştirmek için başka birini işe alamaz.
Reactgular

11

Mrjrdnthms, kabul edilen cevap hakkındaki yorumunda belirttiği gibi, bu artık Chrome'da çalışmıyor (her zaman fareyi harekete geçiriyor), Gustavo'nın yanıtını (jQuery kullandığımdan beri) Chrome davranışını ele almak için uyarladım.

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

  });

});

Array.prototype.equalsFonksiyonu bu geldiğini cevap


1
Bu neredeyse benim için çalıştı, ama [evt.pageX, evt.pageY].equals()komuttan bir hata aldım . Bunu onunla değiştirdim (evt.pageX === currentPos[0] && evt.pageY===currentPos[1])ve her şey iyiydi. :)
user2441511

equalsKod ihtiyaçları Yazımın altındaki linkten eklenecek
Francisco Aquino

Ah, bu açıklıyor. Teşekkürler.
user2441511

1
Mantığı anlayamıyorum. Neden güncellerim currentPosüzerinde mousemove? Bu, bazı sürüklemeleri tıklama olarak ele alacağınız anlamına gelmiyor mu?
nirvana-msu

1
Eğer "mouseup"fareyi hareket ettirirseniz bu ateşlenmez .
ChiMo

9

Tüm bu çözümler ya küçük fare hareketlerini bozar ya da aşırı karmaşıktır.

İşte iki olay dinleyicisi kullanan basit bir uyarlanabilir çözüm. Delta, kodun bir tıklama yerine sürükleme olarak sınıflandırması için yukarı ve aşağı olaylar arasında yatay veya dikey olarak hareket etmeniz gereken piksel cinsinden mesafedir. Bunun nedeni bazen kaldırmadan önce fareyi veya parmağınızı birkaç piksel hareket ettirmenizdir.

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

En iyi cevap!
Giorgio Tempesta

Merhaba @andreyrd, bunun için ne deltakullanıldığını öğrenebilir miyim? mobil cihazda musluk ile bir şey mi?
Haziq

1
@Haziq Bence en iyi çözümler yorumlarda bahsedilen gibi delta"tek bir kene mousemove nedeniyle tıklamak ve bir drag operasyonu almak denemek için sinir bozucu olurdu" için kullanılır
Michael Bykhovtsev

1
Cevabı bir açıklama ile güncelledim. Temel olarak parmağınız 6 pikselden azsa, yine de bir tıklama olarak sayılır. 6 veya daha fazla piksel hareket ederse sürükleme olarak sayılır.
andreyrd

5

Sürüklemeyi algılamak için jQuery'yi 5 piksel x / y ekranıyla kullanma:

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

Sadece sürükleme durumunu filtrelemek için şu şekilde yapın:

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

1

DeltaX ve DeltaY özellikli Pure JS

Bu DeltaX ve DeltaY, bir kene mousemove yerine tıklayıp sürükleme işlemi yaparken sinir bozucu deneyimi önlemek için kabul edilen cevapta bir yorum tarafından önerildiği gibi .

    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

Bir OSM haritasında herkese açık bir işlem için (tıklama üzerine bir işaretçi yerleştirin) soru şuydu: 1) fare aşağı süresinin nasıl belirleneceği (her tıklama için yeni bir işaretleyici hayal edemezsiniz) ve 2) fare aşağı- yukarı hareket ederken hareket eder (yani kullanıcı haritayı sürükler).

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

Bir mesafe eşiği kullanarak sınıf tabanlı vanilya JS için kullanılan başka bir çözüm

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

Ve sınıfa ekleyin (SOMESLIDER_ELEMENT, küresel olmak için belge de olabilir ):

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

Belirli bir öğenin tıklama veya sürükleme davranışını kontrol etmek istiyorsanız, bunu gövdeyi dinlemek zorunda kalmadan yapabilirsiniz.

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

@Przemek'in cevabından,

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.