마우스 "클릭"과 "끌기"를 구별하는 방법


165

내가 사용하는 jQuery.click한편, 내가 마우스 처리 할 필요가 라파엘 그래프에서 마우스 클릭 이벤트를 처리하는 drag이벤트를 마우스 드래그로 구성 mousedown, mouseupmousemove 라파엘있다.

구별하기 어려운 click하고 drag있기 때문에 click도 포함 mousedown& mouseup, 어떻게 자바 스크립트에서 다음 "드래그"& 마우스 "클릭"마우스를 구별 할 수 있습니까?

답변:


192

차이점은 mousemove사이 mousedownmouseup 드래그 드래그 에 있지만 클릭에는 없다는 것입니다.

다음과 같이 할 수 있습니다 :

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
드래그를 트리거하려면 mousemove에서 최소 델타 X 또는 Y가 필요합니다. 한 번의 틱 mousemove로 인해 클릭하여 드래그 조작을 시도하는 것이 실망 스러울 것입니다.
Erik Rydgren

12
나는 이것이 최신 크롬에서 더 이상 작동 생각하지 않는다 : 32.0.1700.72 MouseMove 이벤트 화재는 마우스 나하지 이동 여부
mrjrdnthms

17
이 허용 응답 코드에 XY 마우스 좌표 사이에 최소 델타 조건을 포함해야 mousedown하고 mouseup대신에 듣는 mousemove플래그를 설정하는 이벤트입니다. 또한 @mrjrdnthms
Billybobbonnet에서

2
Chrome 56.0.2924.87 (64 비트)을 실행 중이며 @mrjrdnthms에서 설명하는 문제가 발생하지 않습니다.
jkupczak

1
@AmerllicA이 그 사용 사례에 흥미 그러나 당신이 mouseenter와하는 MouseLeave 이벤트를 볼 수 있었다, 아마 버그하지만 예상되는 동작하지 않습니다
Rivenfall

37

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

클릭하는 동안 마우스를 약간 움직이더라도이라고 표시 drag됩니다. 다른 의견과 마찬가지로 추가 범위가 필요할 수 있습니다.
ChiMo

@ChiMo 내가 사용하고하고있어 처음부터 마우스 위치를 저장하는 evt두 번째의 위치와 비교 evt예를 들어, 그래서, : if (evt.type === 'mouseup' || Math.abs(evt1.pageX - evt2.pageX) < 5 || Math.abs(evt1.pageY - evt2.pageY) < 5) { ....
구스타보 로드리게스

1
나는이 질문에 대한 다른 모든 대답을 시도했지만 이것은를 확인할 때 작동하는 유일한 방법이며 .on('mouseup mousemove touchend touchmove')위치 변수를 설정하지 않습니다. 훌륭한 솔루션!
TheThirdMan

때때로 요소를 클릭하면 "evt.type"이 mouseup 대신 "mousemove"를 반환합니다. 그 문제를 어떻게 해결할 수 있습니까?
Libu Mathew

27

클리너 ES2015

let drag = false;

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

다른 사람들이 언급했듯이 버그가 발생하지 않았습니다.


6
작은 움직임으로 클릭이 발생합니다.
Amir Keibi

1
@AmirKeibi 당신은 mousemoves의 수를 계산 (또는 두 번의 클릭 사이의 거리를 계산하지만 잔인한 것) 수
Rivenfall

19

이것은 잘 작동합니다. 허용 된 답변과 유사하지만 (jQuery를 사용하더라도) isDragging새 마우스 위치가 mousedown이벤트 와 다른 경우에만 플래그가 재설정됩니다 . 허용되는 답변과 달리, 이것은 최신 버전의 Chrome에서 작동하며 mousemove마우스의 이동 여부에 관계없이 실행됩니다.

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

mousemove약간의 공차를 추가 하려면 좌표 체크인을 조정할 수도 있습니다 (예 : 작은 움직임은 드래그가 아닌 클릭으로 처리).


12

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>

이것은 @ wong2가 그의 대답에서 한 일을 직접 복제했지만 RxJ로 변환되었습니다.

의 흥미로운 사용 sample. sample오퍼레이터는 소스로부터의 최신의 값을 (뜻 mergemousedownmousemove) 때 내부 관찰 (그것을 방출 mouseup) 방출한다.


22
상사가 나를 대신 할 다른 사람을 고용 할 수 없도록 모든 코드를 관찰 가능 항목으로 작성합니다.
Reactgular

11

mrjrdnthms가 허용 된 답변에 대한 그의 의견에서 지적한 것처럼, 이것은 더 이상 Chrome에서 작동하지 않으며 (항상 마우스 이동을 발생시킵니다) Chrome 동작을 해결하기 위해 Gustavo의 답변을 조정했습니다 (jQuery를 사용하기 때문에).

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.equals기능이에서 오는 대답


1
이것은 거의 나를 위해 일했지만 [evt.pageX, evt.pageY].equals()명령 에서 오류가 발생했습니다 . 나는 그것을로 바꾸었고 (evt.pageX === currentPos[0] && evt.pageY===currentPos[1])모든 것이 좋았습니다. :)
user2441511

equals코드의 요구가 내 게시물의 하단에있는 링크에서 추가 할
시스코 키노을

아, 그 설명입니다. 감사.
user2441511

1
논리를 이해하지 못하는 것 같습니다. 왜 업데이트합니까 currentPosmousemove? 이것이 드래그를 클릭으로 취급한다는 의미는 아닙니까?
너바나

1
"mouseup"마우스를 움직이고있는 동안에는 작동하지 않습니다 .
ChiMo

9

이러한 모든 솔루션은 작은 마우스 움직임으로 인해 깨지거나 지나치게 복잡합니다.

다음은 두 개의 이벤트 리스너를 사용하는 간단한 적응 형 솔루션입니다. 델타는 코드가 클릭이 아닌 드래그로 코드를 분류하기 위해 위쪽 및 아래쪽 이벤트 사이에서 가로 또는 세로로 이동해야하는 거리 (픽셀 단위)입니다. 마우스 나 손가락을 들어 올리기 전에 몇 픽셀 움직일 때가 있기 때문입니다.

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, delta이것에 무엇이 사용 되는지 알 수 있습니까? 휴대 기기의 탭과 관련이 있습니까?
Haziq

1
@Haziq 나는 최고의 솔루션에 대한 의견에서 언급 한 사람들 delta이 "한 번의 마우스 이동으로 인해 클릭하여 드래그 조작을 시도하는 것이 좌절 될 것"이라고 생각합니다.
Michael Bykhovtsev

1
설명으로 답변을 업데이트했습니다. 기본적으로 손가락이 6 픽셀 미만인 경우 여전히 클릭 수로 계산됩니다. 6 픽셀 이상 이동하면 드래그로 계산됩니다.
andreyrd

5

드래그를 감지하기 위해 5 픽셀 x / y theshold와 함께 jQuery 사용 :

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

드래그 케이스를 필터링하려면 다음과 같이하십시오.

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

1

DeltaX 및 DeltaY가 포함 된 순수 JS

이 DeltaX 및 DeltaY 는 한 번의 마우스 이동으로 인해 클릭하고 끌기 조작을 할 때 실망스러운 경험을 피하기 위해 허용 된 답변 의 의견 에서 제안한 바와 같습니다 .

    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

OSM 맵 (클릭시 마커 위치)에 대한 공개 조치의 경우 1) 마우스 다운 지속 시간을 결정하는 방법-> (각 클릭마다 새로운 마커를 만드는 것을 상상할 수 없음) 2) 아래-> 위로 마우스를 움직입니다 (예 : 사용자가지도를 끌고 있음).

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

거리 임계 값을 사용하는 클래스 기반 바닐라 JS를 사용하는 또 다른 솔루션

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

그리고 클래스에 추가하십시오 (SOMESLIDER_ELEMENT는 전 세계적 으로 문서화 될 수 있습니다).

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

특정 요소의 클릭 또는 드래그 동작을 확인하려면 본문을 듣지 않아도됩니다.

$(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의 답변에서

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.