Cách phân biệt chuột nhấp chuột và


165

Tôi sử dụng jQuery.clickđể xử lý sự kiện click chuột trên Raphael đồ thị, trong khi đó, tôi cần phải xử lý chuột dragsự kiện, chuột kéo bao gồm mousedown, mouseupmousemovetrong Raphael.

Thật khó để phân biệt clickdragclickcũng chứa mousedown& mouseup, Làm cách nào tôi có thể phân biệt chuột "nhấp" & chuột "kéo" trong Javascript?

Câu trả lời:


192

Tôi nghĩ rằng sự khác biệt là có một mousemovegiữa mousedownmouseuptrong một kéo, nhưng không phải trong một nhấp chuột.

Bạn có thể làm một cái gì đó như thế này:

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
Chỉ cần nhớ yêu cầu delta X hoặc Y tối thiểu trên mousemove để kích hoạt kéo. Sẽ nản lòng khi cố gắng nhấp và nhận thao tác kéo thay vì một mousemove một tích tắc
Erik Rydgren

12
Tôi không nghĩ rằng nó hoạt động nữa trong chrome mới nhất: 32.0.1700.72 Mousemove kích hoạt dù bạn có di chuyển chuột hay không
mrjrdnthms

17
Mã câu trả lời được chấp nhận này phải bao gồm một điều kiện delta tối thiểu giữa các tọa độ chuột XY tại mousedownmouseupthay vì nghe mousemovesự kiện để đặt cờ. Hơn nữa, nó sẽ khắc phục sự cố được đề cập bởi @mrjrdnthms
Billybobbonnet

2
Tôi đang chạy Chrome 56.0.2924.87 (64-bit) và tôi không gặp phải sự cố mà @mrjrdnthms đang mô tả.
jkupczak

1
@AmerllicA đây có thể không phải là lỗi nhưng là hành vi được mong đợi, tuy nhiên bạn có thể theo dõi các sự kiện của mouseenter và mouseleave nếu điều đó thú vị với trường hợp sử dụng của bạn
Rivenfall

37

Trong trường hợp bạn đang sử dụng 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);
  });
});

Ngay cả khi bạn di chuyển chuột một chút trong khi nhấp chuột, điều này sẽ nói drag. Một phạm vi bổ sung như các ý kiến ​​khác đang nói có thể cần thiết ở đây.
ChiMo

@ChiMo Những gì tôi đang sử dụng là lưu trữ vị trí chuột từ cái đầu tiên evtvà so sánh với vị trí của cái thứ hai evt, vì vậy, ví dụ : if (evt.type === 'mouseup' || Math.abs(evt1.pageX - evt2.pageX) < 5 || Math.abs(evt1.pageY - evt2.pageY) < 5) { ....
Gustavo Coleues

1
Tôi đã thử tất cả các câu trả lời khác cho câu hỏi này và đây là câu trả lời duy nhất hoạt động khi kiểm tra .on('mouseup mousemove touchend touchmove')và trên hết là không đặt các biến vị trí. Giải pháp tuyệt vời!
TheThirdMan

Đôi khi, khi tôi nhấp vào một phần tử, "evt.type" sẽ trả về "mousemove" thay vì trên mouseup. Làm thế nào tôi có thể giải quyết vấn đề đó?
Libu Mathew

27

Máy hút bụi ES2015

let drag = false;

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

Không gặp bất kỳ lỗi nào, như những người khác nhận xét.


6
Điều này chịu đựng các nhấp chuột với di chuyển nhỏ.
Amir Keibi

1
@AmirKeibi bạn có thể đếm số lượng mousemove (hoặc thậm chí tính khoảng cách giữa hai lần nhấp nhưng điều đó sẽ quá mức cần thiết)
Rivenfall

19

Điều này sẽ làm việc tốt. Tương tự như câu trả lời được chấp nhận (mặc dù sử dụng jQuery), nhưng isDraggingcờ chỉ được đặt lại nếu vị trí chuột mới khác với vị trí trên mousedownsự kiện. Không giống như câu trả lời được chấp nhận, nó hoạt động trên các phiên bản Chrome gần đây, nơi mousemoveđược bắn bất kể chuột có được di chuyển hay không.

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

Bạn cũng có thể điều chỉnh kiểm tra tọa độ mousemovenếu bạn muốn thêm một chút dung sai (nghĩa là coi các chuyển động nhỏ là nhấp chuột, không kéo).


12

Nếu bạn cảm thấy thích sử dụng 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>

Đây là bản sao trực tiếp những gì @ wong2 đã làm trong câu trả lời của anh ấy, nhưng đã được chuyển đổi thành RxJs.

Cũng thú vị sử dụng sample. Các samplenhà điều hành sẽ mất giá trị mới nhất từ nguồn ( mergecủa mousedownmousemove) và phát ra nó khi bên trong quan sát được ( mouseup) phát ra.


22
Tôi viết tất cả mã của mình bằng các vật quan sát để ông chủ của tôi không thể thuê người khác thay thế tôi.
Phản ứng

11

Như mrjrdnthms đã chỉ ra trong nhận xét của mình về câu trả lời được chấp nhận, điều này không còn hoạt động trên Chrome nữa (nó luôn kích hoạt mousemove), tôi đã điều chỉnh câu trả lời của Gustavo (vì tôi đang sử dụng jQuery) để giải quyết hành vi của 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);

  });

});

Các Array.prototype.equalschức năng đến từ này câu trả lời


1
Điều này gần như làm việc cho tôi, nhưng tôi đã nhận được một lỗi từ [evt.pageX, evt.pageY].equals()lệnh. Tôi đã thay thế nó bằng (evt.pageX === currentPos[0] && evt.pageY===currentPos[1]), và tất cả đều tốt. :)
user2441511

Các equalsnhu cầu mã để được bổ sung từ liên kết ở dưới cùng của bài viết của tôi
Francisco Aquino

Ah, điều đó giải thích nó. Cảm ơn.
user2441511

1
Tôi dường như không thể hiểu logic. Tại sao bạn cập nhật currentPosvào mousemove? Điều này không có nghĩa là bạn sẽ coi một số lần kéo là nhấp chuột sao?
niết bàn-msu

1
Điều này sẽ không kích hoạt nếu bạn "mouseup"vẫn di chuyển chuột.
ChiMo

9

Tất cả các giải pháp này hoặc phá vỡ các chuyển động chuột nhỏ, hoặc quá phức tạp.

Đây là một giải pháp thích ứng đơn giản bằng cách sử dụng hai trình lắng nghe sự kiện. Delta là khoảng cách tính bằng pixel mà bạn phải di chuyển theo chiều ngang hoặc chiều dọc giữa các sự kiện lên và xuống cho mã để phân loại nó dưới dạng kéo thay vì nhấp chuột. Điều này là do đôi khi bạn sẽ di chuyển chuột hoặc ngón tay của bạn một vài pixel trước khi nâng nó lên.

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

Cho đến nay câu trả lời tốt nhất!
Giorgio Tempesta

Xin chào @andreyrd, tôi có thể biết những gì deltađược sử dụng cho việc này không? đó là một cái gì đó để làm với tap trong thiết bị di động?
Haziq

@Haziq Tôi nghĩ như mọi người đã đề cập trong các bình luận về các giải pháp hàng đầu deltađược sử dụng cho "Sẽ nản lòng khi cố gắng nhấp và thực hiện thao tác kéo thay vì một mousemove"
Michael Bykhovtsev

Tôi cập nhật câu trả lời với một lời giải thích. Về cơ bản nếu ngón tay của bạn dưới 6 pixel, nó vẫn sẽ được tính là một lần nhấp. Nếu nó di chuyển 6 pixel trở lên, nó sẽ được tính là một lực cản.
andreyrd

5

Sử dụng jQuery với ngưỡng 5 pixel x / y để phát hiện lực cản:

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

Nếu chỉ để lọc trường hợp kéo, hãy làm như thế này:

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

1

JS thuần túy với DeltaX và DeltaY

DeltaX và DeltaY này như được đề xuất bởi một nhận xét trong câu trả lời được chấp nhận để tránh trải nghiệm bực bội khi cố gắng nhấp và thực hiện thao tác kéo thay vì một mousemove.

    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

Đối với hành động công khai trên bản đồ OSM (định vị điểm đánh dấu khi nhấp), câu hỏi là: 1) cách xác định thời lượng chuột xuống-> lên (bạn không thể tưởng tượng việc tạo điểm đánh dấu mới cho mỗi lần nhấp) và 2) đã làm chuột di chuyển trong khi xuống-> lên (tức là người dùng đang kéo bản đồ).

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

Một giải pháp khác sử dụng cho vanilla dựa trên lớp bằng cách sử dụng ngưỡng khoảng cách

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

Và thêm vào lớp (SOMESLIDER_ELEMENT cũng có thể là tài liệu toàn cầu):

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

Nếu bạn muốn kiểm tra hành vi nhấp hoặc kéo của một yếu tố cụ thể, bạn có thể thực hiện việc này mà không cần phải lắng nghe cơ thể.

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

từ câu trả lời của @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
);

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.