为什么在Firefox中dragend事件的event.clientX显示为0?

33

无论当前位置在哪里,dragend的提示显示mouseX为零。在Chrome中正常工作,所以不确定我做错了什么。

function move(e,obj,but){
    if(typeof(obj) === 'string'){
        obj = document.getElementById(obj) ;
    }
    
    if(typeof(but) === 'string'){
        but = document.getElementById(but) ;
    }

    //elementCoord(but) ;//get the current coords of the button &
    elementCoord(obj) ;//the container
    
    e = e || window.event ;
    var mouseX = e.clientX ;
    var mouseY = e.clientY ;
            
    //alert('mouseX='+mouseX+', but.XCoord '+but.XCoord) ;
    var diffX = Math.abs(obj.XCoord - mouseX) ;
    var diffY = Math.abs(obj.YCoord - mouseY) ;
    
    but.addEventListener("dragend",function(evt){
        evt = evt || window.event ;
        mouseX = evt.clientX ;
        mouseY = evt.clientY ;
        obj.style.left = mouseX - diffX + 'px';
        obj.style.top = mouseY - diffY + 'px';
        alert('mouseX='+mouseX+' diffX='+diffX) ;
        }
    ,false) ;
    
}

忘了提到,elementCoord 只是将偏移量添加为属性的对象。在所有浏览器中都可以正常使用。


4
dragend 事件适用于源元素,即被拖动的元素。使用 drop 事件的 clientXclientY 来获取目标元素的坐标,而不是使用 dragend - Jay
3
这是 Bug 地址:https://bugzilla.mozilla.org/show_bug.cgi?id=505521。 - forresto
1
浏览器差异:在IE10中,“dragstart”,“dragend”,“drop”中的clientX相同,但在Chrome / Firefox中,“dragend”相对于拖动源(向右拖动时,clientX为负)。 - Corey Alix
提醒大家注意,在记录拖动开始时点击元素的位置时,也应优先使用clientXclientY而不是xy - user1935043
即使是“拖动”事件,我仍然遇到了这个问题。 - Cels
我知道你提出这个问题已经8年了,而且它被报告给FF已经11年了,但是我终于想出了一个有工作演示的解决方案,希望这可以帮到你! - HoldOffHunger
7个回答

11

这是Firefox的官方问题 -- Bugzilla: Bug # 505521, 在HTML5拖动事件期间设置屏幕坐标。我会引用jbmj的话来总结,并加粗他们引用的原始开发人员...

我简直不能相信11年前的这条评论
"请注意,它没有指定应将属性设置为什么,只是应该进行设置,而我们目前将它们设置为0。"
仍然是最先进的状态。

受Jay的评论启发,我使用“drop”事件。但那只是一条评论,所以让我把它梳理成一个答案。

我们的问题:dragend事件的e.clientY和e.clientX设置为0。

我们将如何解决它:document的drop事件也会在我们正在拖动的元素的dragend事件完全相同时触发。并且:drop将具有e.clientY和e.clientX的正确值。

两个工作演示,100%仅使用JavaScript的解决方案:SO代码片段和JSBin。 SO代码片段控制台有时会在控制台中吞噬拖动的元素,而JSBin给了我更一致的结果。

var startx = 0;
var starty = 0;
dragStartHandler = function(e) {
  startx = e.clientX;
  starty = e.clientY;
}

dragOverHandler = function(e) {
  e.preventDefault();
  return false;
}

dragEndHandler = function(e) {
  if(!startx || !starty) {
    return false;
  }
  
  var diffx = e.clientX - startx;
  var diffy = e.clientY - starty;
  
  var rect = e.target.getBoundingClientRect();

var offset = { 
                top: rect.top + window.scrollY, 
                left: rect.left + window.scrollX, 
            };
  
  var newleft = offset.left + diffx;
  var newtop = offset.top + diffy;
  
  e.target.style.position = 'absolute';
  e.target.style.left = newleft + 'px';
  e.target.style.top = newtop + 'px';
  
  startx = 0;
  starty = 0;
}

document.getElementsByClassName("draggable")[0].addEventListener('dragstart', dragStartHandler);

document.addEventListener('dragover', dragOverHandler);
document.addEventListener('drop', dragEndHandler);
.draggable {
  border: 1px solid black;
  cursor: move;
  width:250px;
};
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  
  <BR><BR><BR>

  <div id="draggable1" class="draggable" draggable="true">
    Hey, try to drag this element!
  </div>
  
</body>
</html>

  • dragStartHandler() :该函数绑定在可拖动元素上。在这里,我们只记录开始时的当前x/y坐标。
  • dragOverHandler() :该函数绑定在文档上,以便覆盖默认的拖放行为。这是执行任何类型的拖放操作所必需的。
  • dragEndHandler() :该函数绑定在documentdrop事件上。通常情况下,我们希望将其绑定到元素的dragend事件上,但由于缺少clientYclientX,因此将其绑定到文档上。它实际上只是在调用dragend时执行你想要发生的所有操作,只不过有了x/y坐标。

9

有一个Firefox的旧错误,与用户指针信息相关的拖动事件无法提供

我已经发现,现在Firefox几乎所有与拖动相关的事件都会发布用户指针信息:

  • "dragstart"
  • "dragenter"
  • "dragleave"
  • "dragover"
  • "drop"
  • "dragend"
  • "drag"

在Firefox 99版本中测试过

因此,回答最初的问题:event.clientX在Firefox的dragend事件中不再总是0


如果您需要在拖动过程中获取指针信息(例如通常会给您的"drag"),则可以执行以下操作:

  1. 将事件侦听器添加到相当高的EventTarget(例如window)并侦听"dragover"事件。
  • dragover会在您悬停在潜在的放置目标上时触发,这是每个Element
  • 通过向高EventTarget(例如window)添加事件侦听器,您可以利用event bubbling来捕获文档中所有Elementdragover事件
  1. 收益:通过重新使用dragover有效地获取drag事件
window.addEventListener('dragover', (event) => {
  // event.clientX and friends are correctly set!
});

2
这救了我的命! - AlexG
1
我以为自己疯了,但当我查阅资料后发现这确实是事实,谢谢。他们有一个无法修复的14年的漏洞,非常棒的火狐浏览器,干得好! - dankobgd

4

看起来这个漏洞可能会在 Firefox 的核心代码中存在较长一段时间,这里有一个 99% 可用的补丁:

if(/Firefox\/\d+[\d\.]*/.test(navigator.userAgent)
        && typeof window.DragEvent === 'function'
        && typeof window.addEventListener === 'function') (function(){
    // patch for Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=505521
    var cx, cy, px, py, ox, oy, sx, sy, lx, ly;
    function update(e) {
        cx = e.clientX; cy = e.clientY;
        px = e.pageX;   py = e.pageY;
        ox = e.offsetX; oy = e.offsetY;
        sx = e.screenX; sy = e.screenY;
        lx = e.layerX;  ly = e.layerY;
    }
    function assign(e) {
        e._ffix_cx = cx; e._ffix_cy = cy;
        e._ffix_px = px; e._ffix_py = py;
        e._ffix_ox = ox; e._ffix_oy = oy;
        e._ffix_sx = sx; e._ffix_sy = sy;
        e._ffix_lx = lx; e._ffix_ly = ly;
    }
    window.addEventListener('mousemove', update, true);
    window.addEventListener('dragover', update, true);
    // bug #505521 identifies these three listeners as problematic:
    // (although tests show 'dragstart' seems to work now, keep to be compatible)
    window.addEventListener('dragstart', assign, true);
    window.addEventListener('drag', assign, true);
    window.addEventListener('dragend', assign, true);

    var me = Object.getOwnPropertyDescriptors(window.MouseEvent.prototype),
        ue = Object.getOwnPropertyDescriptors(window.UIEvent.prototype);
    function getter(prop,repl) {
        return function() {return me[prop] && me[prop].get.call(this) || Number(this[repl]) || 0};
    }
    function layerGetter(prop,repl) {
        return function() {return this.type === 'dragover' && ue[prop] ? ue[prop].get.call(this) : (Number(this[repl]) || 0)};
    }
    Object.defineProperties(window.DragEvent.prototype,{
        clientX: {get: getter('clientX', '_ffix_cx')},
        clientY: {get: getter('clientY', '_ffix_cy')},
        pageX:   {get: getter('pageX', '_ffix_px')},
        pageY:   {get: getter('pageY', '_ffix_py')},
        offsetX: {get: getter('offsetX', '_ffix_ox')},
        offsetY: {get: getter('offsetY', '_ffix_oy')},
        screenX: {get: getter('screenX', '_ffix_sx')},
        screenY: {get: getter('screenY', '_ffix_sy')},
        x:       {get: getter('x', '_ffix_cx')},
        y:       {get: getter('y', '_ffix_cy')},
        layerX:  {get: layerGetter('layerX', '_ffix_lx')},
        layerY:  {get: layerGetter('layerY', '_ffix_ly')}
    });
})();

请注意,尽管OP的问题仅涉及到“dragend”,但这是适用于所有受影响事件的修复方法。
它从“mousemove”和“dragover”事件中获取鼠标的最后准确坐标,并将其注入到受影响的“dragstart”、“drag”和“dragend”事件中。
请注意,这并不是一个完美的修复方法。x / y坐标可能会稍微偏差。由于“drag”事件在“dragover”之前发生,因此它执行时使用来自上一事件帧的坐标。

这解决了我的问题。谢谢亲爱的先生!我希望Mozilla能尽快解决这个问题。 - Dr.Random

1
更新于2022年3月:这个漏洞终于被分配给了同一个人,他已经阻止了它13年。这是在2021年10月中旬。所以经过13年的时间,解决方案正在路上。

有人检查过他是否还活着吗?这个情况已经一年没有改变了... - Ole Henrik Skogstrøm

0

前几天我也遇到了Firefox的同样问题。

我设法找到了一个解决方法,但这取决于使用全局变量来存储鼠标移动前后的位置。

看起来让事情正常工作的部分是从ondrop事件中获取pageX和pageY值,而不是从ondragend事件中获取。

唯一的问题是ondrop不会存储拖动的元素或原始鼠标位置(因此需要全局变量)。

var dragDetails = {
   target: null,
   orgMouseX: 0,
   orgMouseY: 0,
   desMouseX: 0,
   desMouseY: 0
}

$("targetElement").on("dragstart", function(event) {
   dragDetails.target = this;
   dragDetails.orgMouseX = event.originalEvent.pageX;
   dragDetails.orgMouseY = event.originalEvent.pageY;
});

$("html").on("drop", function(event) {
   dragDetails.desMouseX = event.originalEvent.pageX;
   dragDetails.desMouseY = event.originalEvent.pageY;
   handleDrag();
});

这里有一个在fiddle中的例子:https://jsfiddle.net/L1b6uz2d/2/

它似乎可以在最新版本的Chrome、Firefox、Edge和Internet Explorer上工作(尽管在Internet Explorer上的准确性不太好),并且它可以在Android Chrome上工作。还没有测试其他浏览器,我相信代码可以改进。

我成功地让它在不需要全局变量的情况下工作,但我必须使用ondrop,然后将target、pageX和pageY作为参数传递给ondragend事件(我没有包含fiddle,因为代码非常丑陋)。


0
document.addEventListener("dragover", function( event ) {
      event.preventDefault();
      console.log(event.pageX)
  }, false);

dragover监听器中添加console.log (event.pageX)
http://jsfiddle.net/zfnj5rv4/

OP正在询问如何解决dragend中的event.pageX问题。当完全不同的元素拖动到this上时,dragover事件会发生。那么这应该如何用来解决dragend中缺少event.pageX的问题呢? - HoldOffHunger

-2

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