找到相对于元素的鼠标位置

Ben*_*ock 202 javascript

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

小智 231

由于我没有找到可以复制/粘贴的无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.
}
Run Code Online (Sandbox Code Playgroud)

JSFiddle的完整示例

  • 谢谢你!需要注意的是:您需要 `e.currentTarget`,而不是 e.target。否则子元素会影响它 (8认同)
  • 这个答案应该被投票.谢谢anytimecoder和@ErikKoopmans. (5认同)
  • 您需要 [round the bounding rect](/sf/ask/2861542001/) 以确保使用此方法的整数值。 (3认同)
  • PSA:@mpen的评论已过时。即使滚动,这种方法也能奏效。查看他们的小提琴的更新版本:https://jsfiddle.net/6wcsovp2/ (3认同)
  • 谢谢。当有人希望您导入所有 jQuery 只是为了使用一个无需它即可轻松实现的小功能时,我觉得很令人讨厌。特别是在我看来 jQuery 的设计相当糟糕。 (3认同)
  • 新编辑应解决滚动问题(使用clientX / Y) (2认同)

Spi*_*der 156

对于使用JQuery的人:

有时,当您有嵌套元素时,其中一个元素附加了事件,理解您的浏览器看作父母的内容可能会令人困惑.在这里,您可以指定哪个父级.

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

var x = evt.pageX - $('#element').offset().left;
var y = evt.pageY - $('#element').offset().top;
Run Code Online (Sandbox Code Playgroud)

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

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

或者相对于页面的位置:

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

请注意以下性能优化:

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

通过这种方式,JQuery不必查找#element每一行.

  • 怎么可能是答案,因为这个问题与jQuery无关? (62认同)
  • 因为jQuery答案而被低估了 (12认同)
  • 它对你有用吗@ben,我很欣赏一个"正确的答案",或者对你还没有用的评论.也许我可以进一步帮助. (7认同)
  • 应该避免在事件处理程序中使用$('selector'),除非你想要非常缓慢的代码.预先选择元素,并在处理程序中使用预先选择的引用.同样的$(窗口)做一次并缓存它. (7认同)
  • @Black 但有些人在 javascript 而不是 jquery 中提问,因为 jquery 在他们的环境中可能不兼容。 (4认同)
  • 这不是必要的提及:如果你复制粘贴上面的代码,记得纠正`var y =(evt.pageY - $('#element').offset().left)+ $(window).scrollTop ();`到`var y =(evt.pageY - $('#element').offset().top)+ $(window).scrollTop();` (2认同)

小智 56

以下计算与canvas元素的鼠标位置关系:

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

在此示例中,this引用example元素,并且eonmousemove事件.

  • 也许是我,但使用关键字"this"的两行代码缺少上下文? (83认同)
  • 如果某些后代元素使用相对或绝对定位,则这不起作用. (20认同)
  • 您可以将`this`切换为`e.currentTarget`,它将获得相对于您添加事件侦听器的元素的位置. (11认同)
  • 绝对缺乏背景.'e'是事件,'this'是事件与`var example = document.getElementById('example')相关联的元素; example.onmousemove = function(e){var X = e.pageX - this.offsetLeft; var Y = e.pageY - this.offsetTop; }` (9认同)
  • 什么?IE9支持Canvas,你做错了什么? (3认同)
  • 这似乎给予了与元素的*visible*部分相关的位置,而不是整个元素.如果在视口中看不到元素的一部分并且有滚动条等,是否有办法适应这种情况? (2认同)
  • **注意:** [`element.offsetLeft`](https://dev.mozilla.org/Web/API/HTMLElement/offsetLeft "MDN Web Docs") 仅返回到 **最近的父** 元素的距离。要计算到整个视口的距离,请使用 [`element.getClientRects().left`](https://dev.mozilla.org/Web/API/Element/getClientRects "MDN Web Docs") (2认同)

Atr*_*sis 31

我在2010年发现了这个问题,但似乎没有人满意它:纯粹的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,
  }; 

}
Run Code Online (Sandbox Code Playgroud)

  • 我不认为它考虑了`html`元素偏移 (3认同)
  • 只需一点点改动,就可以很好地与滚动容器配合使用:用`while(reference)`替换`while(reference!== undefined)`。至少在Chrome浏览器中,offsetParent最终不会是undefined,而是null。只需检查“引用”是否真实即可确保它可以与“未定义”和“空”一起使用。 (3认同)

Dav*_*ith 19

可以在这里找到关于这个问题难度的好文章:http://www.quirksmode.org/js/events_properties.html#position

使用那里描述的技术,您可以在文档中找到鼠标位置.然后你只需检查它是否在元素的边界框内,你可以通过调用element.getBoundingClientRect()找到它,它将返回一个具有以下属性的对象:{bottom,height,left,right,top,width }.从那里可以轻松地弄清楚你的元素内部是否发生过.


Fab*_*rts 11

我尝试了所有这些解决方案,并且由于使用矩阵转换的容器(panzoom库)进行了特殊设置而无法使用。即使缩放和平移,这也会返回正确的值:

mouseevent(e) {
 const x = e.offsetX
 const y = e.offsetY
}
Run Code Online (Sandbox Code Playgroud)

但前提是没有孩子。可以通过使用CSS使这些孩子对事件“不可见”来规避此问题:

#container.element-dragging *:not(.dragging) {
   pointer-events: none;
}
Run Code Online (Sandbox Code Playgroud)

在dragstart / dragstop函数中,.element-dragging在容器和.dragging元素本身上切换类。

  • 这绝对是在2019年做到这一点的方式。其他答案都充满了旧的沉重行李。 (3认同)
  • @MarkE 答案很简单:[它是实验性的](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetX)。有些人可能关心,其他人可能不关心,但这是事实。 (2认同)

Mat*_*att 7

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

FOllowing为我解决了它:

        x = e.pageX - this.offsetLeft - $(elem).offset().left;
        y = e.pageY - this.offsetTop - $(elem).offset().top;
Run Code Online (Sandbox Code Playgroud)

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


Joh*_*ohn 7

我遇到了这个问题,但为了让我的情况下工作(使用的dragover一个DOM元素上(不是画布在我的情况)),我发现,你只需要使用offsetXoffsetY对的dragover鼠标事件.

onDragOver(event){
 var x = event.offsetX;
 var y = event.offsetY;
}
Run Code Online (Sandbox Code Playgroud)

  • event.offsetX相对于鼠标指针当前所在的元素,而不是附加了dragover侦听器的元素.如果你没有嵌套的元素结构,那么这很好,但如果你这样做,那么这是行不通的. (6认同)
  • 这对我很有用.为什么没有人赞成这个?我想知道是否有任何陷阱.如果我发现任何我会报告回来! (2认同)

Flo*_*anB 5

上述答案都不是令人满意的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);
}
Run Code Online (Sandbox Code Playgroud)

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

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


She*_*ohn 5

摘自本教程,由于顶级评论进行了更正:

function getMousePos( canvas, evt ) {
    var rect = canvas.getBoundingClientRect();
    return {
        x: Math.floor( ( evt.clientX - rect.left ) / ( rect.right - rect.left ) * canvas.width ),
        y: Math.floor( ( evt.clientY - rect.top ) / ( rect.bottom - rect.top ) * canvas.height )
    };
}
Run Code Online (Sandbox Code Playgroud)

在画布上使用如下:

var canvas = document.getElementById( 'myCanvas' );
canvas.addEventListener( 'mousemove', function( evt ) {
    var mousePos = getMousePos( canvas, evt );
} );
Run Code Online (Sandbox Code Playgroud)


lam*_*345 5

canvas.onmousedown = function(e) {
    pos_left = e.pageX - e.currentTarget.offsetLeft;
    pos_top = e.pageY - e.currentTarget.offsetTop;
    console.log(pos_left, pos_top)
}
Run Code Online (Sandbox Code Playgroud)

HTMLElement.offsetLeft

HTMLElement.offsetLeft只读属性返回当前元素的左上角偏移到内左侧的像素的数量HTMLElement.offsetParent的节点。

对于块级元素,offsetTopoffsetLeftoffsetWidthoffsetHeight描述元素相对于 的边框框offsetParent

然而,对于行内的元素(如span),其可以包从一行到下一,offsetTop并且offsetLeft描述第一边界框中的位置(使用Element.getClientRects()以获得其宽度和高度),而offsetWidthoffsetHeight描述的边界的边界框的尺寸(用于Element.getBoundingClientRect()获取其位置)。因此,左边、顶部、宽度和高度为offsetLeftoffsetTopoffsetWidth和 的offsetHeight框将不是带有换行文本的跨度的边界框。

HTMLElement.offsetTop

HTMLElement.offsetTop只读属性返回电流元件相对于所述的顶部的距离offsetParent的节点。

鼠标事件.pageX

pageX只读属性返回事件相对于整个文档的像素的坐标的X(水平)。此属性考虑了页面的任何水平滚动。

鼠标事件.pageY

MouseEvent.pageY只读属性返回Y(垂直)在事件相对于整个文档的像素的坐标。此属性考虑了页面的任何垂直滚动。

更多解释请参见 Mozilla 开发者网络:

https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY https://开头developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetLeft https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop


Joh*_* T. 5

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

只有3条规则:

  1. 请根据期间尽量少mousemovetouchmove事件。
  2. 在做尽可能mousedowntouchstart事件。
  3. 取消传播并阻止触摸事件的默认设置,以防止鼠标事件也在混合设备上触发。

不用说,事件更复杂,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();
}
Run Code Online (Sandbox Code Playgroud)
div {
  background: yellow;
  height: 100px;
  left: 50px;
  position: absolute;
  top: 80px;
  user-select: none; /* Disable text selection */
  -ms-user-select: none;
  width: 100px;
}
Run Code Online (Sandbox Code Playgroud)
<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.
Run Code Online (Sandbox Code Playgroud)

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

<div @mousedown="handleMouseDown"
     @mousemove="handleMouseMove"
     @mouseup="handleMouseUp"
     @touchstart.stop.prevent="handleTouchStart"
     @touchmove.stop.prevent="handleTouchMove"
     @touchend.stop.prevent="handleTouchEnd">
Run Code Online (Sandbox Code Playgroud)


小智 5

如果您想获取与一个元素相关的 layerX 和 layerY,也许您可​​以尝试?

let bbox_rect = document.getElementById("dom-ID").getBoundingClientRect()
let layerX = e.clientX-bbox_rect.left
let layerY = e.clientY-bbox_rect.top
Run Code Online (Sandbox Code Playgroud)