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

时间:2010-07-13 04:47:52

标签: javascript

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

27 个答案:

答案 0 :(得分:147)

对于使用JQuery的人:

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

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

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

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

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

或相对于页面的位置:

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

请注意以下性能优化:

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

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

答案 1 :(得分:138)

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

{% block extra_js %}
    <script src="{%static 'js/map.js' %}" type="text/javascript"></script>
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAZY5nutUGjjf4D...endOFKEY&callback=initMap"async defer></script>
{% endblock %}

JSFiddle of full example

答案 2 :(得分:52)

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

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

在此示例中,this引用example元素,e代表onmousemove事件。

答案 3 :(得分:23)

我在2010年发现了这个问题,但似乎没有人满意它:纯粹的javascript中没有答案,当引用元素嵌套在其他可以绝对定位的内容时返回相对坐标。这是我的解决方案:

function getRelativeCoordinates (event, element){

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

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

  let reference = element.offsetParent;

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

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

}

答案 4 :(得分:17)

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

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

答案 5 :(得分:7)

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

FOllowing为我解决了这个问题:

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

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

答案 6 :(得分:6)

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

offsetY

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

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

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

答案 8 :(得分:4)

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

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

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

#container.element-dragging *:not(.dragging) {
   pointer-events: none;
}

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

答案 9 :(得分:4)

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

只有3条规则:

  1. mousemovetouchmove事件期间尽量少做。
  2. mousedowntouchstart事件中尽可能多地做。
  3. 取消传播并防止触摸事件的默认设置,以防止鼠标事件也在混合设备上触发。
  4. 毋庸置疑,touch事件会使事情变得更复杂,因为它们可能不止一个,而且它们比鼠标事件更灵活(复杂)。我在这里只会涉及到一次触摸。是的,我是懒惰的,但它是最常见的触摸类型,所以就在那里。

    &#13;
    &#13;
    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();
    }
    &#13;
    div {
      background: yellow;
      height: 100px;
      left: 50px;
      position: absolute;
      top: 80px;
      user-select: none; /* Disable text selection */
      -ms-user-select: none;
      width: 100px;
    }
    &#13;
    <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.
    &#13;
    &#13;
    &#13;

    喜欢使用Vue.js?我做!然后您的HTML将如下所示:

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

答案 10 :(得分:3)

如果你想得到一个元素相关的layerX和layerY,也许你可以试试:

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

答案 11 :(得分:3)

我意识到我有点迟了,但这适用于PURE javascript,如果元素大于视口并且用户已滚动,它甚至会为您提供元素内指针的坐标。

var element_offset_x ; // The distance from the left side of the element to the left of the content area


....// some code here (function declaration or element lookup )



element_offset_x = element.getBoundingClientRect().left  -  document.getElementsByTagName("html")[0].getBoundingClientRect().left  ;

....// code here 




function mouseMoveEvent(event) 
{
   var pointer_location = (event.clientX + window.pageXOffset) - element_offset_x ; 
}
  

工作原理。

我们要做的第一件事是获取HTML元素(内容区域)相对于当前视口的位置。如果页面有滚动条并且滚动,那么getBoundingClientRect().left为html标记返回的数字将为负数。然后,我们使用此数字来计算元素与内容区域左侧之间的距离。使用element_offset_x = element.getBoundingClientRect().left......;

了解元素与内容区域的距离。 event.clientX给出了指针距视口的距离。重要的是要理解视口和内容区域是两个不同的实体,如果页面滚动,视口可以移动。因此,即使页面滚动,clientX也将返回相同的编号。

为了弥补这一点,我们需要将指针的x位置(相对于视口)添加到视口的x位置(相对于内容区域)。视口的X位置位于 window.pageXOffset.

答案 12 :(得分:2)

由于event.offsetX和event.offsetY,可以获得画布内的鼠标坐标。这是一个证明我的观点的小片段:

c=document.getElementById("c");
ctx=c.getContext("2d");
ctx.fillStyle="black";
ctx.fillRect(0,0,100,100);
c.addEventListener("mousemove",function(mouseEvt){
  // the mouse's coordinates on the canvas are just below
  x=mouseEvt.offsetX;
  y=mouseEvt.offsetY;
  // the following lines draw a red square around the mouse to prove it
  ctx.fillStyle="black";
  ctx.fillRect(0,0,100,100);
  ctx.fillStyle="red";
  ctx.fillRect(x-5,y-5,10,10);
});
  
body {
  background-color: blue;
}

canvas {
  position: absolute;
  top: 50px;
  left: 100px;
}
<canvas id="c" width="100" height="100"></canvas>
    

答案 13 :(得分:2)

根据@ Spider的解决方案,我的非JQuery版本就是这样:

// Get the container element's bounding box
var sides = document.getElementById("container").getBoundingClientRect();

// Apply the mouse event listener
document.getElementById("canvas").onmousemove = (e) => {
  // Here 'self' is simply the current window's context
  var x = (e.clientX - sides.left) + self.pageXOffset;
  var y = (e.clientY - sides.top) + self.pageYOffset;
}

这适用于滚动和缩放(在这种情况下,有时它会返回浮点数)。

答案 14 :(得分:2)

取自this tutorial,由于最高评论而做出更正:

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

在画布上使用如下:

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

答案 15 :(得分:2)

你可以通过

获得它
var element = document.getElementById(canvasId);
element.onmousemove = function(e) {
    var xCoor = e.clientX;
    var yCoor = e.clientY;
}

答案 16 :(得分:1)

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

<强> HTMLElement.offsetLeft

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

对于块级元素,offsetTopoffsetLeftoffsetWidthoffsetHeight描述元素相对于offsetParent的边框。< / p>

但是,对于可以从一行换行到下一行的内联级元素(例如span),offsetTopoffsetLeft描述第一个边框的位置(使用Element.getClientRects()获取其宽度和高度,而offsetWidthoffsetHeight描述边界框的尺寸(使用Element.getBoundingClientRect()获取其位置)。因此,具有左侧,顶部,宽度和高度offsetLeftoffsetTopoffsetWidthoffsetHeight的框不会是包含文本的跨度的边界框。

<强> HTMLElement.offsetTop

HTMLElement.offsetTop只读属性返回当前元素相对于offsetParent节点顶部的距离。

<强> MouseEvent.pageX

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

<强> MouseEvent.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

答案 17 :(得分:1)

function myFunction(e) {
    var x =  e.clientX - e.currentTarget.offsetLeft ; 
    var y = e.clientY - e.currentTarget.offsetTop ;
}

这行得通!

答案 18 :(得分:1)


const findMousePositionRelativeToElement = (e) => {
    const xClick = e.clientX - e.currentTarget.offsetLeft;
    const yClick = e.clientY - e.currentTarget.offsetTop;
    console.log(`x: ${xClick}`);
    console.log(`y: ${yClick}`);

    // or
    const rect = e.currentTarget.getBoundingClientRect();
    const xClick2 = e.clientX - rect.left;
    const yClick2 = e.clientY - rect.top;
    console.log(`x2: ${xClick2}`);
    console.log(`y2: ${yClick2}`);
}

答案 19 :(得分:1)

您可以简单地将 jQuery 的 event.pageXevent.pageY 与 jQuery 的 offset() 方法结合使用,以获取鼠标相对于元素的位置。

  $(document).ready(function() {
    $("#myDiv").mousemove(function(event){            
      var X = event.pageX - $(this).offset().left;
      var Y = event.pageY - $(this).offset().top;
      $(".cordn").text("(" + X + "," + Y + ")");
    });
  });

您可以在此处查看示例:How to find mouse position relative to element

答案 20 :(得分:0)

原帖回答说把它放在iframe中。更好的解决方案是在填充设置为0px的画布上使用事件offsetX和offsetY。

<html>
<body>
<script>

var main=document.createElement('canvas');
main.width="200";
main.height="300";
main.style="padding:0px;margin:30px;border:thick dashed red";
document.body.appendChild(main);

// adding event listener

main.addEventListener('mousemove',function(e){
    var ctx=e.target.getContext('2d');
    var c=Math.floor(Math.random()*0xFFFFFF);
    c=c.toString(16); for(;c.length<6;) c='0'+c;
    ctx.strokeStyle='#'+c;
    ctx.beginPath();
    ctx.arc(e.offsetX,e.offsetY,3,0,2*Math.PI);
    ctx.stroke();
    e.target.title=e.offsetX+' '+e.offsetY;
    });

// it worked! move mouse over window

</script>
</body>
</html>

答案 21 :(得分:0)

你必须知道页面的结构,因为如果你的画布是div的孩子,而div又是另一个div的孩子......那么故事会变得更加复杂。这是我的一个画布的代码,它位于div的两个级别内:

canvas.addEventListener("click", function(event) {
var x = event.pageX - (this.offsetLeft + this.parentElement.offsetLeft);
var y = event.pageY - (this.offsetTop + this.parentElement.offsetTop);
console.log("relative x=" + x, "relative y" + y);

});

答案 22 :(得分:0)

这就是我得到的。

    $(".some-class").click(function(e) {

    var posx = 0;
    var posy = 0;

    posx = e.pageX;
    posy = e.pageY;

    alert(posx);
    alert(posy);
});

答案 23 :(得分:0)

我实现了另一个我认为非常简单的解决方案,因此我想与大家分享。

所以,对我来说,问题是被拖动的div对于鼠标光标将跳到 0,0 。因此,我需要捕获div上的鼠标位置以调整divs的新位置。

我阅读了divs PageX和PageY并根据其设置了顶部和左侧,然后获取值以调整坐标以将光标保持在div中的初始位置,我使用了onDragStart侦听器并存储了 e.nativeEvent.layerX e.nativeEvent.layerY ,只有在初始触发条件下,鼠标才能在可拖动div中定位。

示例代码:

 onDrag={(e) => {
          let newCoords;
          newCoords = { x: e.pageX - this.state.correctionX, y: e.pageY - this.state.correctionY };
          this.props.onDrag(newCoords, e, item.id);
        }}
        onDragStart={
          (e) => {
            this.setState({
              correctionX: e.nativeEvent.layerX,
              correctionY: e.nativeEvent.layerY,
            });
          }

我希望这对遇到过同样问题的人有所帮助:)

答案 24 :(得分:0)

您可以使用getBoudingClientRect()父级的relative

document.addEventListener("mousemove", (e) => {
  let xCoord = e.clientX - e.target.getBoundingClientRect().left + e.offsetX
  let yCoord = e.clientY - e.target.getBoundingClientRect().top + e.offsetY
  console.log("xCoord", xCoord, "yCoord", yCoord)
})

答案 25 :(得分:0)

基于@Patrick Boos solution,但解决了中间滚动条的潜在问题。

export function getRelativeCoordinates(event: MouseEvent, referenceElement: HTMLElement) {
  const position = {
    x: event.pageX,
    y: event.pageY,
  };

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

  let reference = referenceElement.offsetParent as HTMLElement;

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

  const scrolls = {
    left: 0,
    top: 0,
  };

  reference = event.target as HTMLElement;
  while (reference) {
    scrolls.left += reference.scrollLeft;
    scrolls.top += reference.scrollTop;
    reference = reference.parentElement as HTMLElement;
  }

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

答案 26 :(得分:0)

我必须使用滚动条将光标放在非常宽的div内。目的是将元素拖动到div的任何位置。

要使鼠标位置位于滚动中较深的位置。

$('.canvas').on('mousemove', function(e){
    $(dragElement).parent().css('top', e.currentTarget.scrollTop + e.originalEvent.clientY );
    $(dragElement).parent().css('left', e.currentTarget.scrollLeft + e.originalEvent.clientX )
});