Javascript 查找鼠标相对于元素的位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3234256/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:47:21  来源:igfitidea点击:

Find mouse position relative to element

javascript

提问by Ben Shelock

I want to make a little painting app using canvas. So I need to find the mouse's position on the canvas.

我想使用画布制作一个小绘画应用程序。所以我需要在画布上找到鼠标的位置。

采纳答案by sparkyspider

For people using JQuery:

对于使用 JQuery 的人:

Sometimes, when you have nested elements, one of them with the event attached to it, it can be confusing to understand what your browser sees as the parent. Here, you can specify which parent.

有时,当您有嵌套元素(其中一个附加了事件)时,理解浏览器将什么视为父元素可能会令人困惑。在这里,您可以指定哪个父级。

You take the mouse position, and then subtract it from the parent element's offset position.

您获取鼠标位置,然后从父元素的偏移位置中减去它。

var x = evt.pageX - $('#element').offset().left;
var y = evt.pageY - $('#element').offset().top;

If you're trying to get the mouse position on a page inside a scrolling pane:

如果您尝试在滚动窗格内的页面上获取鼠标位置:

var x = (evt.pageX - $('#element').offset().left) + self.frame.scrollLeft();
var y = (evt.pageY - $('#element').offset().top) + self.frame.scrollTop();

Or the position relative to the page:

或者相对于页面的位置:

var x = (evt.pageX - $('#element').offset().left) + $(window).scrollLeft();
var y = (evt.pageY - $('#element').offset().top) + $(window).scrollTop();

Note the following performance optimisation:

请注意以下性能优化:

var offset = $('#element').offset();
// Then refer to 
var x = evt.pageX - offset.left;

In this way, JQuery does not have to look up #elementfor each line.

这样,JQuery 就不用#element逐行查找了。

Update

更新

There is a newer, JavaScript-only version below by @anytimecoder below for browsers supporting getBoundingClientRect().

下面的@anytimecoder 为支持 getBoundingClientRect() 的浏览器提供了一个更新的纯 JavaScript 版本。

回答by anytimecoder

As I didn't find a jQuery-free answer that I could copy/paste, here's the solution I used:

由于我没有找到可以复制/粘贴的无 jQuery 答案,这是我使用的解决方案:

function clickEvent(e) {
  // e = Mouse click event.
  var rect = e.target.getBoundingClientRect();
  var x = e.clientX - rect.left; //x position within the element.
  var y = e.clientY - rect.top;  //y position within the element.
}

JSFiddle of full example

完整示例的 JSFiddle

回答by LindaQ

The following calculates the mouse position relation to the canvas element:

下面计算鼠标与画布元素的位置关系:

var example = document.getElementById('example'); 
example.onmousemove = function(e) { 
    var x = e.pageX - this.offsetLeft; 
    var y = e.pageY - this.offsetTop; 
}

In this example, thisrefers to the exampleelement, and eis the onmousemoveevent.

在这个例子中,this指的是example元素,eonmousemove事件。

回答by Atrahasis

There is no answer in pure javascript that returns relative coordinates when the reference element is nested inside others which can be with absolute positioning. Here is a solution to this scenario:

当参考元素嵌套在其他可以具有绝对定位的元素中时,纯 javascript 中没有返回相对坐标的答案。这是这种情况的解决方案:

function getRelativeCoordinates (event, referenceElement) {

  const position = {
    x: event.pageX,
    y: event.pageY
  };

  const offset = {
    left: referenceElement.offsetLeft,
    top: referenceElement.offsetTop
  };

  let reference = referenceElement.offsetParent;

  while(reference){
    offset.left += reference.offsetLeft;
    offset.top += reference.offsetTop;
    reference = reference.offsetParent;
  }

  return { 
    x: position.x - offset.left,
    y: position.y - offset.top,
  }; 

}

回答by David W. Keith

A good write up of the difficulty of this problem can be found here: http://www.quirksmode.org/js/events_properties.html#position

可以在此处找到有关此问题难度的详细说明:http: //www.quirksmode.org/js/events_properties.html#position

Using the technique that is described there you can find the mouses position in the document. Then you just check to see if it is inside the bounding box of your element, which you can find by calling element.getBoundingClientRect()which will return an object with the following properties: { bottom, height, left, right, top, width }. From there it is trivial to figure out if the even happened inside your element or not.

使用那里描述的技术,您可以在文档中找到鼠标位置。然后,您只需检查它是否在元素的边界框内,您可以通过调用element.getBoundingClientRect()which 将返回具有以下属性的对象来找到它:{ bottom, height, left, right, top, width }. 从那里确定甚至是否发生在您的元素内部是微不足道的。

回答by Fabian von Ellerts

I tried all these solutions and due to my special setup with a matrix transformed container (panzoom library) none worked. This returns the correct value, even if zoomed and paned:

我尝试了所有这些解决方案,但由于我使用矩阵转换容器(panzoom 库)的特殊设置,没有任何效果。即使缩放和平移,这也会返回正确的值:

mouseevent(e) {
 const x = e.offsetX,
       y = e.offsetY
}

But only if there are no child elements in the way. This can be circumvented by making them 'invisible' to the event, using CSS:

但前提是没有子元素挡路。这可以通过使用 CSS 使它们对事件“不可见”来规避:

.child {
   pointer-events: none;
}

回答by John

I came across this question, but in order to make it work for my case (using dragover on a DOM-element (not being canvas in my case)), I found that you only have have to use offsetXand offsetYon the dragover-mouse event.

我遇到了这个问题,但为了使它适用于我的情况(在 DOM 元素上使用dragover(在我的情况下不是画布)),我发现你只需要在dragover-mouse 事件上使用offsetXoffsetY.

onDragOver(event){
 var x = event.offsetX;
 var y = event.offsetY;
}

回答by Matt

I +1' Mark van Wyk's answer as it got me in the right direction, but didn't quite solve it for me. I still had an offset on painting in elements contained within another element.

我 +1' Mark van Wyk 的回答,因为它让我朝着正确的方向前进,但并没有完全为我解决。我仍然对包含在另一个元素中的元素进行绘画偏移。

FOllowing solved it for me:

以下为我解决了这个问题:

        x = e.pageX - this.offsetLeft - $(elem).offset().left;
        y = e.pageY - this.offsetTop - $(elem).offset().top;

In other words - i simply stacked all the offsets from all elements nested

换句话说 - 我只是堆叠了所有嵌套元素的所有偏移量

回答by FlorianB

None of the above answers are satisfactory IMO, so here's what I use:

以上答案都不是 IMO 令人满意的,所以这是我使用的:

// Cross-browser AddEventListener
function ael(e, n, h){
    if( e.addEventListener ){
        e.addEventListener(n, h, true);
    }else{
        e.attachEvent('on'+n, h);
    }
}

var touch = 'ontouchstart' in document.documentElement; // true if touch device
var mx, my; // always has current mouse position IN WINDOW

if(touch){
    ael(document, 'touchmove', function(e){var ori=e;mx=ori.changedTouches[0].pageX;my=ori.changedTouches[0].pageY} );
}else{
    ael(document, 'mousemove', function(e){mx=e.clientX;my=e.clientY} );
}

// local mouse X,Y position in element
function showLocalPos(e){
    document.title = (mx - e.getBoundingClientRect().left)
        + 'x'
        + Math.round(my - e.getBoundingClientRect().top);
}

And if you ever need to know the current Y scrolling position of page :

如果您需要知道页面的当前 Y 滚动位置:

var yscroll = window.pageYOffset
        || (document.documentElement && document.documentElement.scrollTop)
        || document.body.scrollTop; // scroll Y position in page

回答by John T.

For those of you developing regular websites or PWAs (Progressive Web Apps) for mobile devices and/or laptops/monitors with touch screens, then you have landed here because you might be used to mouse events and are new to the sometimes painful experience of Touch events... yay!

对于那些为移动设备和/或带有触摸屏的笔记本电脑/显示器开发常规网站或 PWA(渐进式 Web 应用程序)的人,那么您来到这里是因为您可能已经习惯了鼠标事件,并且对触摸有时会带来痛苦的体验不熟悉事件……耶!

There are just 3 rules:

只有3条规则:

  1. Do as little as possible during mousemoveor touchmoveevents.
  2. Do as much as possible during mousedownor touchstartevents.
  3. Cancel propagation and prevent defaults for touch events to prevent mouse events from also firing on hybrid devices.
  1. 请根据期间尽量少mousemovetouchmove事件。
  2. 在做尽可能mousedowntouchstart事件。
  3. 取消传播并阻止触摸事件的默认设置,以防止鼠标事件也在混合设备上触发。

Needless to say, things are more complicated with touchevents because there can be more than one and they're more flexible (complicated) than mouse events. I'm only going to cover a single touch here. Yes, I'm being lazy, but it's the most common type of touch, so there.

不用说,事件更复杂,touch因为事件可能不止一个,而且它们比鼠标事件更灵活(复杂)。我只打算在这里介绍一下。是的,我很懒,但这是最常见的触摸类型,所以就这样。

var posTop;
var posLeft;
function handleMouseDown(evt) {
  var e = evt || window.event; // Because Firefox, etc.
  posTop = e.target.offsetTop;
  posLeft = e.target.offsetLeft;
  e.target.style.background = "red";
  // The statement above would be better handled by CSS
  // but it's just an example of a generic visible indicator.
}
function handleMouseMove(evt) {
  var e = evt || window.event;
  var x = e.offsetX; // Wonderfully
  var y = e.offsetY; // Simple!
  e.target.innerHTML = "Mouse: " + x + ", " + y;
  if (posTop)
    e.target.innerHTML += "<br>" + (x + posLeft) + ", " + (y + posTop);
}
function handleMouseOut(evt) {
  var e = evt || window.event;
  e.target.innerHTML = "";
}
function handleMouseUp(evt) {
  var e = evt || window.event;
  e.target.style.background = "yellow";
}
function handleTouchStart(evt) {
  var e = evt || window.event;
  var rect = e.target.getBoundingClientRect();
  posTop = rect.top;
  posLeft = rect.left;
  e.target.style.background = "green";
  e.preventDefault(); // Unnecessary if using Vue.js
  e.stopPropagation(); // Same deal here
}
function handleTouchMove(evt) {
  var e = evt || window.event;
  var pageX = e.touches[0].clientX; // Touches are page-relative
  var pageY = e.touches[0].clientY; // not target-relative
  var x = pageX - posLeft;
  var y = pageY - posTop;
  e.target.innerHTML = "Touch: " + x + ", " + y;
  e.target.innerHTML += "<br>" + pageX + ", " + pageY;
  e.preventDefault();
  e.stopPropagation();
}
function handleTouchEnd(evt) {
  var e = evt || window.event;
  e.target.style.background = "yellow";
  // Yes, I'm being lazy and doing the same as mouseout here
  // but obviously you could do something different if needed.
  e.preventDefault();
  e.stopPropagation();
}
div {
  background: yellow;
  height: 100px;
  left: 50px;
  position: absolute;
  top: 80px;
  user-select: none; /* Disable text selection */
  -ms-user-select: none;
  width: 100px;
}
<div 
  onmousedown="handleMouseDown()" 
  onmousemove="handleMouseMove()"
  onmouseout="handleMouseOut()"
  onmouseup="handleMouseUp()" 
  ontouchstart="handleTouchStart()" 
  ontouchmove="handleTouchMove()" 
  ontouchend="handleTouchEnd()">
</div>
Move over box for coordinates relative to top left of box.<br>
Hold mouse down or touch to change color.<br>
Drag to turn on coordinates relative to top left of page.

Prefer using Vue.js? I do! Then your HTML would look like this:

更喜欢使用Vue.js?我愿意!那么您的 HTML 将如下所示:

<div @mousedown="handleMouseDown"
     @mousemove="handleMouseMove"
     @mouseup="handleMouseUp"
     @touchstart.stop.prevent="handleTouchStart"
     @touchmove.stop.prevent="handleTouchMove"
     @touchend.stop.prevent="handleTouchEnd">