Як розрізнити клацання миші та перетягування миші


165

Я використовую jQuery.clickдля обробки події клацання миші на Raphael графіку, тим часом, мені потрібно обробляти миші dragподії миші перетягнути складається з mousedown, mouseupі mousemoveв Рафаелі.

Важко розрізнити , clickі dragтому clickтакож містять mousedownі mouseup, як я можу відрізнити миша «клацання» & миша «перетягнути» , то в JavaScript?

Відповіді:


192

Я думаю, що різниця полягає в тому, що є mousemoveміж mousedownі mouseupв перетягуванні, але не в клацанні.

Ви можете зробити щось подібне:

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
Просто пам’ятайте, що для запуску перетягування потрібен мінімум дельти X або Y в русі миші. Буде неприємно намагатися натиснути і отримати операцію перетягування замість цього через рух одного кліща мишкою
Ерік Ридгрен

12
Я не думаю, що це працює більше в останньому хромі: 32.0.1700.72 Миші рухаються мишею, чи рухаєте ви мишкою чи ні
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" повертає "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
Це страждає від клацань крихітними рухами.
Амір Кейбі

1
@AmirKeibi можна було порахувати кількість рухів миші (або навіть обчислити відстань між двома клацаннями, але це було б надмірно)
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 зробив у своїй відповіді, але перетворився на RxJs.

Також цікаве використання sample. sampleОператор прийме останнє значення з джерела ( mergeз mousedownі mousemove) і випромінює його , коли внутрішні спостережуваний ( mouseup) випускає.


22
Я пишу весь код зі спостережуваними документами, щоб мій начальник не міг найняти когось іншого, щоб замінити мене.
Реакційний

11

Як зазначає mrjrdnthms у своєму коментарі до прийнятої відповіді, це більше не працює в Chrome (він завжди спрацьовує з рухом миші), я адаптував відповідь Густаво (оскільки я використовую jQuery) для вирішення поведінки 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);

  });

});

Array.prototype.equalsФункція виходить з цього відповіді


1
Це майже працювало для мене, але я отримав помилку від [evt.pageX, evt.pageY].equals()команди. Я замінив це на (evt.pageX === currentPos[0] && evt.pageY===currentPos[1]), і все було добре. :)
користувач2441511

В equalsпотреби коду повинні бути додана за посиланням в нижній частині мого поста
Francisco Акіно

Ах, це пояснює це. Дякую.
користувач2441511

1
Я, здається, не розумію логіку. Чому ви оновлюєте currentPosна mousemove? Чи це не означає, що ви ставитесь до деяких перетяжок як кліків?
nirvana-msu

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, використовуються для "Було б неприємно намагатись натиснути та отримати операцію перетягування замість цього за допомогою миші одним натиском"
Михайло Биховцев

1
Я оновив відповідь поясненням. В основному, якщо ваш палець менше 6 пікселів, він все одно вважатиметься клацанням. Якщо він переміститься на 6 або більше пікселів, він вважатиметься перетягуванням.
Андрейрд

5

Використання jQuery з 5-піксельним x / y для визначення перетягування:

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

Чистий JS з DeltaX та DeltaY

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.