event.clientX показывает как 0 в Firefox для события драгенд

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

}

Предупреждение от dragend показывает mouseX как ноль, независимо от того, где он находится в данный момент. Это прекрасно работает в Chrome, поэтому не уверен, что я делаю не так.

Забыл упомянуть, elementCoord просто получает смещение объекта, добавляя его в качестве свойства. Он отлично работает во всех браузерах.

7 ответов

Официально это проблема Firefox - Bugzilla: Ошибка № 505521, установка координат экрана во время события перетаскивания HTML5. Я процитирую jbmj, чтобы подвести итог, и выделю жирным шрифтом оригинального разработчика, которого они цитируют...

Я не могу поверить, что этот комментарий
" Обратите внимание, что он не указывает, какие свойства должны быть установлены, просто они должны быть установлены, и в настоящее время мы устанавливаем их на 0. "
от 11 лет назад все еще является современным.

Комментарий Джея меня вдохновил на использование события "drop". Но это был всего лишь комментарий, так что позвольте мне превратить его в ответ.

Наша проблема: dragend событие имеет e.clientY а также e.clientX установлен на 0.

Как мы это решим: documentс dropсобытие также запускается в то же время, что и элемент, который мы перетаскиваемdragendсобытие. А также:drop будут иметь правильные значения для e.clientY а также e.clientX.

Две рабочие демонстрации, решение на 100% только для JavaScript: SO Code Snippet и 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(): Это привязано к документу, поэтому мы можем переопределить поведение dragover по умолчанию. Это необходимо для перетаскивания любого типа.
  • dragEndHandler(): Это связано с documentс drop. Обычно мы хотим, чтобы это было привязано кelementс dragend, но с тех пор clientY а также clientXотсутствуют, привязываем к документу. Это именно то, что вы хотели бы получить при вызове dragend, за исключением того, что у вас есть координаты x/y.

В firefox существует старая ошибка, связанная с тем, что события перетаскивания не предоставляют информацию об указателе пользователя.

Я обнаружил, что почти все события, связанные с перетаскиванием, в Firefox теперь публикуют информацию об указателе пользователя:

  • "dragstart"
  • "dragenter"
  • "dragleave"
  • "drop"
  • "dragend"

Проверено в Firefox версии 99.

Итак, чтобы ответить на исходный вопрос: event.clientXбольше не всегда будет 0в dragendв фаерфоксе


Если вам нужно получить информацию об указателе во время перетаскивания (например, что "drag"обычно дает вам), то вы можете сделать следующее:

  1. Добавьте прослушиватель событий на достаточно высокий уровень (например, ) и слушайте "dragover"События.
  • срабатывает всякий раз, когда вы находитесь над потенциальной целью сброса, то есть каждый раз
  • Добавляя прослушиватель событий в высокий EventTarget(например window) вы можете использовать восхождение событий для захвата всех событий для всех Elementс в document
  1. Прибыль: эффективно получить dragсобытие путем перепрофилирования dragover
      window.addEventListener('dragover', (event) => {
  // event.clientX and friends are correctly set!
});

Похоже, что эта ошибка может оставаться основной в 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 могут немного отличаться. Поскольку событие «перетаскивания» происходит перед «перетаскиванием», оно выполняется с координатами из предыдущего кадра события.

Обновление, март 2022 г.: ошибка наконец-то была назначена тому же человеку, который блокировал ее в течение 13 лет. Это было в середине октября 2021 года. Итак, спустя 13 лет решение уже на подходе.

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

добавить console.log (event.pageX) в прослушиватель dragover http://jsfiddle.net/zfnj5rv4/

Я столкнулся с той же проблемой с Firefox на днях.

Мне удалось найти обходной путь, хотя это зависит от использования глобальной переменной для хранения мыши до и после позиций.

Казалось, что работало то, что нужно получить значения pageX и pageY из события ondrop вместо события 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();
});

Вот пример в скрипке: https://jsfiddle.net/L1b6uz2d/2/

Похоже, что он работает на последних версиях Chrome, Firefox, Edge и Internet Explorer (точность не так хороша в Internet Explorer, хотя) и работает на Android Chrome. Другие еще не тестировали, и я уверен, что код можно улучшить.

Мне удалось заставить его работать без необходимости использования глобальной переменной, но мне пришлось использовать ondrop, а затем передать target, pageX и pageY в качестве параметров событию ondragend (я не включил скрипту, потому что код был очень уродливым)

Не используйте 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

Другие вопросы по тегам