如何区分鼠标“点击”和“拖动”

时间:2011-05-18 09:00:31

标签: javascript javascript-events

我使用jQuery.click来处理Raphael图上的鼠标点击事件,同时,我需要处理鼠标drag事件,鼠标拖动包含mousedownmouseup和拉斐尔mousemove

很难区分clickdrag因为click还包含mousedown& mouseup,我如何区分鼠标“点击”和鼠标“拖”然后在Javascript?

19 个答案:

答案 0 :(得分:174)

我认为不同之处在于拖动时mousemovemousedown之间存在mouseup,而不是点击。

您可以这样做:

var flag = 0;
var element = xxxx;
element.addEventListener("mousedown", function(){
    flag = 0;
}, false);
element.addEventListener("mousemove", function(){
    flag = 1;
}, false);
element.addEventListener("mouseup", function(){
    if(flag === 0){
        console.log("click");
    }
    else if(flag === 1){
        console.log("drag");
    }
}, false);

答案 1 :(得分:32)

如果您已经在使用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);
  });
});

答案 2 :(得分:17)

这应该运作良好。与接受的答案类似(尽管使用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中的坐标检查(即将微小的动作视为咔嗒声,而不是拖拽)。

答案 3 :(得分:12)

正如mrjrdnthms在他对已接受答案的评论中所指出的,这不再适用于Chrome(它总是触发鼠标移动),我已经改编了Gustavo的答案(因为我使用的是jQuery)来解决Chrome的行为。< / p>

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功能来自此answer

答案 4 :(得分:12)

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

这是使用两个事件侦听器的简单可调整解决方案。 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!
  }
});

答案 5 :(得分:10)

如果您想使用Rxjs

&#13;
&#13;
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");
  });
&#13;
<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>
&#13;
&#13;
&#13;

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

同样有趣的使用samplesample运算符将从源(mergemousedown的{​​{1}}获取最新值,并在内部可观察量(mousemove)发出时发出

答案 6 :(得分:7)

更清洁的ES2015

let drag = false;

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

正如其他人所说,没有遇到任何错误。

答案 7 :(得分:4)

使用带有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");
});

答案 8 :(得分:3)

如果只是为了过滤掉案例,请按以下步骤操作:

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

答案 9 :(得分:2)

就是这么简单

var dragged = false
window.addEventListener('mousedown', function () { dragged = false })
window.addEventListener('mousemove', function () { dragged = true })
window.addEventListener('mouseup', function() {
        if (dragged == true) { return }
        console.log("CLICK!! ")
})

老实说,您不想添加允许小动作的阈值。以上是点击所有桌面界面的正确、正常、感觉。

试试吧。

如果您愿意,您可以轻松add an event

答案 10 :(得分:2)

您可以这样做:

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)
  }
})
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;
}
<html>
  <body>
    <div id="div">Click me or drag me.</div>
  </body>
</html>

答案 11 :(得分:1)

带有DeltaX和DeltaY的纯JS

comment在接受的答案中建议使用此DeltaX和DeltaY,以避免在尝试单击并进行拖动操作时因一次鼠标移动而令人沮丧。

[{'Age': ' 23', 'Name': ' David'},
 {'Age': ' 21', 'Name': ' Ally'},
 {'Age': ' 20', 'Name': ' John'},
 {'Age': ' 22', 'Name': ' Peter'}]

答案 12 :(得分:1)

基于this的答案,我是在我的React组件中完成的:

export default React.memo(() => {
    const containerRef = React.useRef(null);

    React.useEffect(() => {
        document.addEventListener('mousedown', handleMouseMove);

        return () => document.removeEventListener('mousedown', handleMouseMove);
    }, []);

    const handleMouseMove = React.useCallback(() => {
        const drag = (e) => {
            console.log('mouse is moving');
        };

        const lift = (e) => {
            console.log('mouse move ended');
            window.removeEventListener('mousemove', drag);
            window.removeEventListener('mouseup', this);
        };

        window.addEventListener('mousemove', drag);
        window.addEventListener('mouseup', lift);
    }, []);

    return (
        <div style={{ width: '100vw', height: '100vh' }} ref={containerRef} />
    );
})

答案 13 :(得分:0)

最近在树列表中遇到了同样的问题,用户可以在其中单击项目或拖动它,制作这个小 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;
    }
  }
}

在这里你可以看到它在工作中:

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

答案 14 :(得分:0)

下面的代码是检测mouseupmousedown的移动。

<块引用>

它适用于大多数情况。这也取决于 关于您如何将 mouseevent 视为点击。

在 JavaScript 中,检测非常简单。它不关心如何 长按或在 mousedown 和 mouseup 之间移动。 当您的鼠标在之间移动时,Event.detail 不会重置为 1 mousedownmouseup

如果需要区分点击和长按,则需要 检查 event.timeStamp 中的差异。

// ==== add the code at the begining of your coding ====
let clickStatus = 0;
(() => {
    let screenX, screenY;
    document.addEventListener('mousedown', (event) => ({screenX, screenY} = event), true); 
    document.addEventListener('mouseup', (event) => (clickStatus = Math.abs(event.screenX - screenX) + Math.abs(event.screenY - screenY) < 3), true);
})();
// ==== add the code at the begining of your coding ====

$("#draggable").click(function(event) {
    if (clickStatus) {
        console.log(`click event is valid, click count: ${event.detail}`)
    } else {
        console.log(`click event is invalid`)
    }

})
<!doctype html>
<html lang="en">
<!-- coding example from https://jqueryui.com/draggable/ -->
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>jQuery UI Draggable - Default functionality</title>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
  <link rel="stylesheet" href="/resources/demos/style.css">
  <style>
  #draggable { width: 150px; height: 150px; padding: 0.5em; }
  </style>
  <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
  <script>
  $( function() {
    $( "#draggable" ).draggable();
  } );
  </script>
</head>
<body>
 
<div id="draggable" class="ui-widget-content">
  <p>Drag me around</p>
</div>
 
 
</body>
</html>

答案 15 :(得分: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
);

答案 16 :(得分: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>

答案 17 :(得分: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也可以是 document 以具有全局性):

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

答案 18 :(得分:0)

对于在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);    

}