如何获取相对于父级div的鼠标坐标?Javascript

33

我目前有一个带有其他元素的div结构。

类似于以下内容:

<div id="container" style="position: relative; width: 500px; height: 500px;">
     <div style="position: absolute; left: 50px; top: 50px;"></div>
     <div style="position: absolute; left: 100px; top: 100px;"></div>
</div>

我正试图获得鼠标相对于带有id为container的div的位置。

到目前为止,我的代码如下:

function onMousemove(event) {

    x = event.offsetX;
    y = event.offsetY;
};

var elem = document.getElementById("container");
elem.addEventListener("mousemove", onMousemove, false);

如果带有id为container的div没有子元素,这样做就可以正常工作。当

容器
具有子元素时,它获取相对于子元素而不是父元素的鼠标坐标。

我的意思是,如果鼠标相对于父元素处于x: 51,y: 51的位置,则使用上面给出的html,它实际上会返回相对于子元素的x: 1,y: 1

我该如何实现我想要的效果,不要使用库。

编辑

tymeJV已经很好地解释了上面发生的事情。

http://jsfiddle.net/N6PJu/1


2
我已经模拟了一个小提琴:http://jsfiddle.net/N6PJu/1 -- 这是否正确地显示了您的错误? - tymeJV
是的,那就是正在发生的事情,谢谢你做到了 :) - GriffLab
6个回答

91

接受的答案在Chrome浏览器中对我无效。以下是我的解决方法:

function relativeCoords ( event ) {
  var bounds = event.target.getBoundingClientRect();
  var x = event.clientX - bounds.left;
  var y = event.clientY - bounds.top;
  return {x: x, y: y};
}

4
非常优雅的解决方案。要回答原始问题,您只需将event.target替换为document.getElementById("container") - homerjam
致相关人士,已接受答案的JSFiddle演示(http://jsfiddle.net/N6PJu/3/)目前在Chrome上无法正常工作。 - Ezra Steinmetz
这个很好用,如果容器被移动,它不会给出错误的坐标! - SWIK
2
需要注意的是,getBoundingClientRect 是昂贵的,因为它会导致布局抖动 - bluenote10

9
本质上:'mouseposition' - 'parent element position' = '相对于父元素的鼠标位置'
所以我修改了你的函数:
function onMousemove(e){
    var m_posx = 0, m_posy = 0, e_posx = 0, e_posy = 0,
           obj = this;
    //get mouse position on document crossbrowser
    if (!e){e = window.event;}
    if (e.pageX || e.pageY){
        m_posx = e.pageX;
        m_posy = e.pageY;
    } else if (e.clientX || e.clientY){
        m_posx = e.clientX + document.body.scrollLeft
                 + document.documentElement.scrollLeft;
        m_posy = e.clientY + document.body.scrollTop
                 + document.documentElement.scrollTop;
    }
    //get parent element position in document
    if (obj.offsetParent){
        do { 
            e_posx += obj.offsetLeft;
            e_posy += obj.offsetTop;
        } while (obj = obj.offsetParent);
    }
    // mouse position minus elm position is mouseposition relative to element:
    dbg.innerHTML = ' X Position: ' + (m_posx-e_posx) 
                  + ' Y Position: ' + (m_posy-e_posy);
}

var elem = document.getElementById('container');
elem.addEventListener('mousemove', onMousemove, false);

var dbg=document.getElementById('dbg');  //debut output div instead of console

这里有一个工作演示fiddle。正如您在代码中所读到的那样,这也查看了文档的滚动位置。
PPK关于“event properties”和“find position”的文章(像往常一样)是很好的阅读材料!
希望这可以帮助! 更新: 我实际上在ppk的代码中发现了一个错误(多么罕见啊!):我更正了错误的varif (!e){e = window.event;}

我相信楼主会感激你的努力(我也一样),但我希望这不会削弱“搜索和发现”的精神,而变成“复制、使用而不理解”。在这里要负责任,楼主;-) - Willem Mulder
1
@WillemMulder:谢谢!代码的完整解释已经在PPK链接中提供。它解释了原理并证明了它,同时(对于读者)指出了所有常见的陷阱。然而,我基本上同意:教他们如何钓鱼更好。但在这种情况下,需要一章来指出所有这些(跨浏览器)陷阱。顺便说一句,我们也不知道问问题者(或任何其他未来的读者)是否会花时间理解代码。 - GitaarLAB
@WillemMulder 我不是复制粘贴,只是学习并自己编写 :) - GriffLab
谢谢。完全解释得很好,我已经点赞了。但是有一个小问题。如果容器被转换为百分比,结果会出现偏移。 - SWIK

8
尽管Nikita Volkov的答案相当扎实,但在某些情况下可能无法按预期工作。
我建议使用currentTarget而不是target:
const handleEvent = (event) => 
{
  const bounds = event.currentTarget.getBoundingClientRect();
  const x = event.clientX - bounds.left;
  const y = event.clientY - bounds.top;
  // Now x and y are the relative coordinates.
}

请注意:
  • event.target是触发事件的元素(例如,用户单击或悬停的元素)。
  • event.currentTarget是附加了事件监听器的元素。

当您想要将侦听器附加到具有多个子元素的某个元素时,这很重要。

例如:

<div onMouseMove={handleEvent} id="div0">
  <div style={{'height': 200, 'width': 200}} id="div1">
  </div>
  <div style={{'height': 200, 'width': 200}} id="div2">
  </div>
</div>

如果您使用event.target并在鼠标悬停在div2上时,您将得到相对于div2的坐标,而您可能希望它们是关于div0的。然后event.currentTarget就可以解决问题了。

1
感谢添加这些信息! - thatman303
1
currentTarget - 这就是解决我的问题的方法。谢谢! - Matley

2

Html

<div id="container" style="position: relative; width: 500px; height: 500px;">
<div style="position: absolute; left: 50px; top: 50px;"></div>
<div style="position: absolute; left: 100px; top: 100px;"></div>
</div>

Javascript

function onMousemove(event) {
    x = event.layerX; // position x relative to div
    y = event.layerY; // position y relative to div 
};

var elem = document.getElementById("container");
elem.addEventListener("mousemove", onMousemove, false);

3
“这个特性不是标准的,也没有进入标准轨道。请不要在面向Web的生产网站上使用它:它无法适用于所有用户。” - mindplay.dk

1
获取screenX和screenY属性来查看鼠标在屏幕上的位置(可能要考虑scrollHeight!)。
然后获取容器元素的左侧和顶部,并计算它们之间的偏移量:这就是元素中鼠标的位置。
有关详细信息,请参见https://developer.mozilla.org/en-US/docs/DOM/MouseEvent

1
使用函数式组件,您可以将useRefclick事件监听器结合使用(而不需要直接引用DOM):
const MyElement = () => {
  const ref = useRef(null);

  useEffect(() => {
    const currentRef = ref.current;
    if (currentRef) {
      const clickListener = (e) => {
        const rightOfElement = currentRef.getBoundingClientRect().right;
        const leftOfElement = currentRef.getBoundingClientRect().left;
        const mousePosXRelativeToElement = e?.clientX ?? 0;

        const mousePosX = rightOfElement - mousePosXRelativeToElement;
        const fullWidth = rightOfElement - leftOfElement;

        // do something, e.g. changing state based on position
        if (mousePosX / fullWidth < 0.5) {
          setTheme(2);
        } else {
          setTheme(1);
        }
      };
      currentRef.addEventListener("click", clickListener);
      return () => {
        currentRef.removeEventListener("click", clickListener);
      };
    }
  }, [setTheme]);

  return (
    <div className="theme-selector-wrapper">
      <div className="theme-selector" ref={ref}></div>
    </div>
  );
};

useEffect中(即元素加载后),为分配给ref元素的元素添加一个监听点击事件的事件监听器。在这个示例中,我根据X位置与元素宽度的比率改变了“主题”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接