如何区分鼠标"点击"和"拖动"

Lee*_*eem 151 javascript dom-events

我使用的jQuery.click处理上拉斐尔图形的鼠标点击事件,同时,我需要处理鼠标drag事件,鼠标拖动由mousedown,mouseupmousemove在拉斐尔.

很难区分click,drag因为click还包含mousedown&mouseup,如何在Javascript中区分鼠标"点击"和鼠标"拖动"?

won*_*ng2 184

我认为不同之处在于存在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)
Run Code Online (Sandbox Code Playgroud)

  • 只需记住在鼠标移动时需要最小的增量X或Y来触发拖动.由于只有一个滴答鼠标移动,尝试点击并获得拖动操作会很令人沮丧 (34认同)
  • 这个接受的答案代码应包括在"mousedown"和"mouseup"的XY鼠标坐标之间的最小增量条件,而不是监听`mousemove`事件来设置标志.而且,它将解决@mrjrdnthms提到的问题 (15认同)
  • 我认为这不再适用于最新的Chrome:32.0.1700.72无论你是否移动鼠标,Mousemove都会触发 (10认同)
  • 我正在运行 Chrome 56.0.2924.87(64 位),但我没有遇到 @mrjrdnthms 所描述的问题。 (3认同)

Gus*_*ues 34

如果您已经在使用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);
  });
});
Run Code Online (Sandbox Code Playgroud)


and*_*yrd 30

所有这些解决方案要么因微小的鼠标移动而中断,要么过于复杂。

这是一个使用两个事件侦听器的简单适应性解决方案。Delta 是以像素为单位的距离,您必须在向上和向下事件之间水平或垂直移动代码才能将其归类为拖动而不是点击。这是因为有时您会在抬起鼠标或手指之前将其移动几个像素。

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!
  }
});
Run Code Online (Sandbox Code Playgroud)

  • 该解决方案将“在按住按钮的同时移动多远多长时间,然后在您开始的地方释放它”也视为单击。 (5认同)
  • 迄今为止最好的答案! (2认同)
  • @Haziq我认为正如人们在顶级解决方案的评论中提到的那样,“delta”用于“由于一键鼠标移动而尝试单击并进行拖动操作会令人沮丧” (2认同)

nir*_*msu 18

这应该很好.与接受的答案类似(尽管使用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 = [];
    });
Run Code Online (Sandbox Code Playgroud)

mousemove如果要添加一点公差,也可以调整坐标检查(即将微小的运动视为咔嗒声,而不是拖拽).


Prz*_*mek 17

清洁ES2015

let drag = false;

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

没有经历任何错误,正如其他人评论.

  • 这是因为点击动作很小。 (3认同)
  • @AmirKeibi你可以计算鼠标移动的次数(或者甚至计算两次点击之间的距离,但这有点矫枉过正) (3认同)

Fra*_*ino 12

正如mrjrdnthms在他对已接受答案的评论中指出的那样,这不再适用于Chrome(它总是触发鼠标移动),我已经改编了Gustavo的答案(因为我使用的是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);

  });

});
Run Code Online (Sandbox Code Playgroud)

Array.prototype.equals功能来自这个答案


Dor*_*rus 11

如果你想使用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");
  });
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

这是@ wong2在答案中所做的直接克隆,但转换为RxJs.

也有趣的用途sample.所述sample操作者将采取的最新值从源(mergemousedownmousemove),并发射它时,可观察到的内(mouseup)发射.

  • 我用可观察的代码编写了所有代码,以便我的老板不能雇用其他人代替我. (18认同)

sil*_*ind 5

使用具有5个像素x / y阈值的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");
});
Run Code Online (Sandbox Code Playgroud)


Zay*_*yan 5

你可以这样做:

var div = document.getElementById("div");
div.addEventListener("mousedown", function() {
  window.addEventListener("mousemove", drag);
  window.addEventListener("mouseup", lift);
  var didDrag = false;
  function drag() {
    //when the person drags their mouse while holding the mouse button down
    didDrag = true;
    div.innerHTML = "drag"
  }
  function lift() {
    //when the person lifts mouse
    if (!didDrag) {
      //if the person didn't drag
      div.innerHTML = "click";
    } else div.innerHTML = "drag";
    //delete event listeners so that it doesn't keep saying drag
    window.removeEventListener("mousemove", drag)
    window.removeEventListener("mouseup", this)
  }
})
Run Code Online (Sandbox Code Playgroud)
body {
  outline: none;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: Arial, Helvetica, sans-serif;
  overflow: hidden;
}
#div {
  /* calculating -5px for each side of border in case border-box doesn't work */
  width: calc(100vw - 10px);
  height: calc(100vh - 10px);
  border: 5px solid orange;
  background-color: yellow;
  font-weight: 700;
  display: grid;
  place-items: center;
  user-select: none;
  cursor: pointer;
  padding: 0;
  margin: 0;
}
Run Code Online (Sandbox Code Playgroud)
<html>
  <body>
    <div id="div">Click me or drag me.</div>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)


Fen*_*nec 5

最近在树列表中遇到了同样的问题,用户可以单击该项目或拖动它,创建了这个小类Pointer并将其放入我的utils.js

function Pointer(threshold = 10) {
  let x = 0;
  let y = 0;

  return {
    start(e) {
     x = e.clientX;
     y = e.clientY;
    },

    isClick(e) {
      const deltaX = Math.abs(e.clientX - x);
      const deltaY = Math.abs(e.clientY - y);
      return deltaX < threshold && deltaY < threshold;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

在这里你可以看到它的工作情况:

function Pointer(threshold = 10) {
  let x = 0;
  let y = 0;

  return {
    start(e) {
     x = e.clientX;
     y = e.clientY;
    },

    isClick(e) {
      const deltaX = Math.abs(e.clientX - x);
      const deltaY = Math.abs(e.clientY - y);
      return deltaX < threshold && deltaY < threshold;
    }
  }
}

const pointer = new Pointer();

window.addEventListener('mousedown', (e) => pointer.start(e))
//window.addEventListener('mousemove', (e) => pointer.last(e))
window.addEventListener('mouseup', (e) => {
  const operation = pointer.isClick(e) 
    ? "Click"
    : "Drag"
  console.log(operation)
})
Run Code Online (Sandbox Code Playgroud)