Firefox中的event.offsetX

48
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script language="javascript">
function main(){
    var canvas = document.getElementById("canvas");
    canvas.addEventListener("mousemove", function(e){
        if (!e) e = window.event;
        var ctx = canvas.getContext("2d");

        var x = e.offsetX;
        var y = e.offsetY;

        ctx.fillRect(x, y, 1, 1);
    });
}
</script>
</head>
<body onload="main();">
<div style="width: 800px; height: 600px; -webkit-transform: scale(0.75,0.75); -moz-transform: scale(0.75,0.75)">
    <canvas id="canvas" width="400px" height="400px" style="background-color: #cccccc;"></canvas>
</div>
</body>
</html>
请考虑上面的简单例子。 请注意,我的 canvas 包含在一个应用了缩放变换的 div 中。 上面的代码在任何基于 Webkit 的浏览器中都能完美地工作。移动鼠标时,它会在画布上绘制点。 不幸的是,在 Firefox 中它不起作用,因为其事件模型不支持 offsetX/Y 属性。 我如何将鼠标坐标从(也许是)event.clientX(在 Firefox 中也受支持)转换成相对于画布的坐标,同时考虑到画布的位置、变换等? 谢谢,Luca。

1
谢天谢地,Firefox 40 支持 offsetX! - Pikamander2
12个回答

78

来自JQuery故障跟踪器页面的信息 - 一个好用的 polyfill 是这样的:

var offX  = (e.offsetX || e.pageX - $(e.target).offset().left);

..其中e是从jQuery事件返回的事件。显然,只有在你的项目中已经引入了jQuery,否则就必须手动执行offset()操作。


2
哇,从所有的答案中,只有这个对我有效。我已经尝试了绝对定位元素,非常感谢你!这是这个话题中最好的答案。 - Denis
15
如果您滚动了页面,这个修复方法将有所帮助:var eoffsetX = (e.offsetX || e.clientX - $(e.target).offset().left + window.pageXOffset), eoffsetY = (e.offsetY || e.clientY - $(e.target).offset().top + window.pageYOffset); - Denis
谢谢@Denis,您添加“y位置”的答案确实帮助我解决了我的问题。 - Anil kumar
9
自从回答发布以来,Bug跟踪页面已经发生了变化。现在建议您使用e.pageX而不是e.clientX,这样可以解决滚动页面的问题。该方法适用于最新版本的Chrome、Firefox和IE浏览器。 - Milanka
似乎自Firefox 39版本以来,该属性现在得到支持。好消息! - danp

36

试试layerX,layerY

var x = (e.offsetX === undefined) ? e.layerX : e.offsetX;
var y = (e.offsetY === undefined) ? e.layerY : e.offsetY;

FIDDLE


2
不幸的是,Firefox 的 layerX 和 layerY 属性与其他浏览器中的属性不同。它们是相对于页面左上角的位置,而不是元素的位置。 - Ivan Kochurkin
5
错误,它们是相对于最近的offsetParent,包括元素本身,只需将"position:relative"添加到您想要用作坐标的元素中即可。 - Ivan Castellanos
对我没用,但这个可以解决问题:https://dev59.com/Jmcs5IYBdhLWcg3wmlWk - Palani
没有在FF 32.0.3上工作。layerX和layerY在事件中不存在。 - cristiano2lopes
1
layerX和offsetX不同,如果目标是内联元素,则偏移量给出光标在元素中的位置,但返回相对于其非内联父容器的layerX。 - Adrian Maire
显示剩余3条评论

21

很不幸,对我来说没有解决方案可用。

我在这里找到了一个好的实现(链接):

var target  = e.target || e.srcElement,
              rect    = target.getBoundingClientRect(),
              offsetX = e.clientX - rect.left,
              offsetY  = e.clientY - rect.top;

e.offsetX   = offsetX;
e.offsetY   = offsetY;

15

遗憾的是,offsetX和layerX并不完全相同。其中,offsetX是在当前元素内的偏移量,而layerX是相对于页面的偏移量。以下是我目前使用的解决方法:

function fixEvent(e) {
    if (! e.hasOwnProperty('offsetX')) {
        var curleft = curtop = 0;
        if (e.offsetParent) {
           var obj=e;
           do {
              curleft += obj.offsetLeft;
              curtop += obj.offsetTop;
           } while (obj = obj.offsetParent);
        }
        e.offsetX=e.layerX-curleft;
        e.offsetY=e.layerY-curtop;
    }
    return e;
}

3
+1 如果有一个不使用jQuery的解决方案,并且保持与“offsetX”和“offsetY”相同的语义。 - Drew Noakes
2
在Firefox 26.0下,事件(e)中没有名为'offsetParent'的字段,因此初始e.offsetParent无法工作('if'被简单忽略)。解决方案可能是使用e.target.offsetParent。 - Alex
@Alex e.target.offsetParent 也不起作用。你有解决这个问题的方法吗? - Langdon
+1 - 这是唯一一个对我有效的解决方案,唯一的变化是使用 e.target.offsetParent 而不是 e.offsetParent。我正在使用 RaphaelJS 在地图上绘制对象。使用这段代码,即使 e.target 有时是正在绘制的圆圈,有时是地图画布,我始终能够获得正确的鼠标坐标。 - igelineau

11

Musa的解决方案中存在一个错误:考虑当e.offsetX === 0e.layerX === undefined时会发生什么...

var x = e.offsetX || e.layerX; // x is now undefined!

更健壮的版本如下:

var x = e.hasOwnProperty('offsetX') ? e.offsetX : e.layerX;
var y = e.hasOwnProperty('offsetY') ? e.offsetY : e.layerY;

或者,因为我们可以假设如果offsetX已定义,那么offsetY也会被定义:

var hasOffset = e.hasOwnProperty('offsetX'),
    x         = hasOffset ? e.offsetX : e.layerX,
    y         = hasOffset ? e.offsetY : e.layerY;

1
这对我不起作用,因为Firefox事件具有“offsetX/Y”,但两者都未定义。就它的价值而言。 - jhoff
1
糟糕的Mozilla!在这种情况下,请适当调整 - 感谢您的标记。 - Mark Whitaker
9
var x = e.offsetX || e.layerX || 0 ;是一个更简单的解决方案。 - GameAlchemist
2
layerX 和 offsetX 不同,如果目标是内联元素,则 offset 给出光标在元素中的位置,但返回相对于其非内联父容器的 layerX。 - Adrian Maire

4

offset 实际上并不直接对应于 layeroffset 属性没有考虑元素的外边距。下面的代码应该会考虑到这一点。

function(e) {
    var x = e.offsetX, y = e.offsetY;
    if(e.hasOwnProperty('layerX')) {
      x = e.layerX - e.currentTarget.offsetLeft;
      y = e.layerY - e.currentTarget.offsetTop;
    }
}

3

由于各种原因,非jQuery版本均无法完全正常工作。然而,在您的帮助下,我已经使其正常运行:

if(!event.hasOwnProperty('offsetX')) {
    event.offsetX = event.layerX - event.currentTarget.offsetLeft;
    event.offsetY = event.layerY - event.currentTarget.offsetTop;
}

@AdrianMaire - 你能具体说明是哪些吗? - Marco Faustinelli

3
我发现这里发布的所有答案,除了EnotionZ和laz brannigan的最后两个答案(之前每个答案都没有得到投票),在包含多个元素的div中都是错误的。在我的情况下,我在单个div内有几个canvas元素,并且我分别监听每个canvas。
经过长时间的试错,我得出的最终正确答案,在FireFox和Chrome中完美地运行并产生相同的结果,如下所示:
//inside my mouse events handler:
var anOffsetX = (inEvent.offsetX !== undefined) ? inEvent.offsetX : (inEvent.layerX - inEvent.target.offsetLeft);
var anOffsetY = (inEvent.offsetY !== undefined) ? inEvent.offsetY : (inEvent.layerY - inEvent.target.offsetTop);

据EnotionZ在他的帖子中指出,这种方法可能也适用于边距等方面,但我没有尝试过。


2

但是,如果没有layerXlayerY字段,你该怎么办?

 var xe=e.offsetX,ye=e.offsetY;
  if(!xe){
     xe=e.clientX - $(e.target).offset().left;
  }
  if(!ye){
    ye= e.clientY - $(e.target).offset().top;
  }

2

在FireFox、Chrome/Edge中,OffsetX/Y行为不同。您可能需要通过添加代码片段来计算值:

const offsetX = evt.clientX - this.myRef.current.getBoundingClientRect().left;
const offsetY = evt.clientY - this.myRef.current.getBoundingClientRect().top;

“myRef”是其最近父元素的引用。因此,偏移量将是该元素(clientX)与其父元素之一之间的距离。

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