为啥 event.clientX 在 firefox 中为 dragend 事件错误地显示为 0?
Posted
技术标签:
【中文标题】为啥 event.clientX 在 firefox 中为 dragend 事件错误地显示为 0?【英文标题】:Why is event.clientX incorrectly showing as 0 in firefox for dragend event?为什么 event.clientX 在 firefox 中为 dragend 事件错误地显示为 0? 【发布时间】:2012-07-24 06:11:59 【问题描述】:来自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
只是获取对象的偏移量,并将其添加为属性。它适用于所有浏览器。
【问题讨论】:
dragend
事件适用于源元素,即被拖动的元素。使用drop
事件的clientX
和clientY
获取目标元素的坐标,而不是使用dragend
。
FWIW,这是错误:bugzilla.mozilla.org/show_bug.cgi?id=505521
浏览器差异:在IE10中clientX在“dragstart”、“dragend”、“drop”中是相同的,但在Chrome/Firefox中“dragend”是相对于拖动源的(向右拖动,clientX为负数) )。
只是提醒人们在drag
部分更喜欢clientX
和clientY
而不是`x`和y
点击开始拖动...
即使drag
事件我也遇到了这个问题
【参考方案1】:
这是 Firefox 的正式问题 -- Bugzilla: Bug #505521, Set screen coordinates during html5 drag event。我会引用 jbmj 来总结一下,我会加粗他们引用的原始开发人员...
我不敢相信这个评论 "请注意,虽然它没有指定属性应该设置什么,只是应该设置它们,我们目前将它们设置为 0。" 从 11 年前开始,它仍然是最先进的。
Jay 的评论启发了我使用“drop”事件。但这只是一个评论,所以让我把它打造成一个答案。
我们的问题:dragend
事件将 e.clientY
和 e.clientX
设置为 0。
我们将如何解决它:document
的 drop
事件也触发,与我们正在拖动的元素的 dragend
事件同时触发。并且:drop
将具有正确的 e.clientY
和 e.clientX
值。
两个工作演示,100% 仅 JavaScript 的解决方案:SO 代码片段和 JSBin。 SO Code Snippet 控制台有时会吞噬控制台中拖动的元素,而 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()
:绑定到document
的drop
。通常,我们希望它绑定到element
的dragend
,但是由于缺少clientY
和clientX
,我们将它绑定到文档。这正是您在调用 dragend 时想要发生的事情,除非您有 x/y 坐标。
【讨论】:
【参考方案2】:前几天我在使用 Firefox 时遇到了同样的问题。
我设法找到了解决方法,尽管它依赖于使用全局变量来存储鼠标前后位置。
似乎使事情正常进行的一点是从 ondrop 事件而不是 ondragend 事件获取 pageX 和 pageY 值。
唯一的问题是 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();
);
这是一个小提琴的例子:https://jsfiddle.net/L1b6uz2d/2/
它似乎可以在最新版本的 Chrome、Firefox、Edge 和 Internet Explorer 上运行(但在 Internet Explorer 上的准确性不那么好),它也可以在 android Chrome 上运行。还没有测试过任何其他的,我相信代码可以改进。
我确实设法让它在不需要全局变量的情况下工作,但我必须使用 ondrop,然后将目标、pageX 和 pageY 作为参数传递给 ondragend 事件(我没有包含小提琴,因为代码很丑)
【讨论】:
【参考方案3】: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的问题。 dragover
发生在一个完全不同的元素被拖过 this
时。应该如何使用它来解决dragend
中缺少的 event.pageX?【参考方案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' 之前,它使用前一个事件框架的坐标执行。
【讨论】:
【参考方案5】:不要使用 e.clientX 或 e.clientY
请改用 e.pageX 和 e.pageY 或 e.targetTouches[0].pageX e.targetTouches[0].pageY(用于触摸屏)。
pageX 指的是文档,clientX 指的是视口。另见:
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX
【讨论】:
【参考方案6】:2022 年 3 月更新:该错误终于被分配给了阻止它 13 年的同一个人。那是 2021 年 10 月中旬。因此,13 年后,解决方案正在酝酿之中。
【讨论】:
以上是关于为啥 event.clientX 在 firefox 中为 dragend 事件错误地显示为 0?的主要内容,如果未能解决你的问题,请参考以下文章
轻松搞懂javascript event对象的clientX,offsetX,screenX,pageX区别
event对象的clientX,offsetX,screenX,pageX和offsetTop,offsetHeight等等
clientX,offsetX,screenX,pageX 区别!
event.offsetX event.pageX event.clientX 和 obj.offsetLeft学习笔记
event.offsetX event.pageX event.clientX 和 obj.offsetLeft学习笔记
event.offsetX event.pageX event.clientX 和 obj.offsetLeft学习笔记