Почему я могу вытащить мышь из области перетаскивания в этом скрипте? - PullRequest
3 голосов
/ 14 декабря 2011

Используя простой сценарий JS от Дэвида Фланагана, чтобы сделать мой букмарклет перетаскиваемым.

Я заметил, что могу перемещать указатель с панели перетаскивания во время нажатия клавиши, и всплывающее окно может или не может следоватьуказатель вокруг или внезапная привязка к указателю.

Общее впечатление от Firefox 8 и 9 не впечатляет.IE8 на XP работает как задумано

Он предназначен для букмарклета, поэтому я не могу использовать такие фреймворки, как jQuery или YUI.

ВОПРОС: Как улучшить липкостьmousedown / drag, чтобы окно оставалось присоединенным к мыши onmousedown и onmousemove с использованием простого JS?

Также, пожалуйста, помогите мне заставить getLeft и getTop работать в IE / Chrome и Fx, чтобы всплывающее окно было ограничено областью просмотра.

СТАРЫЙ ДЕМО ЗДЕСЬ

НОВОЕ И ФИКСИРОВАННОЕ ДЕМО ЗДЕСЬ (спасибо techfoobar)

function getTop(top) {
//  if (console) console.log('y:'+top+':'+document.body.clientHeight);  
  if (top<0) return 0;
  if (top>=(document.body.clientHeight-40)) return document.body.clientHeight-40;
  return top;
}
function getLeft(left) {
//  if (console) console.log('x:'+left+':'+document.body.clientWidth);  
  if (left<0) return 0;
  if (left>=(document.body.clientWidth-500)) return document.body.clientWidth-500;
  return left;
}
// This code is from the book JavaScript: The Definitive Guide, 6th Edition (ISBN #978-0596805524). Copyright 2011 by David Flanagan.
function getScrollOffsets(w) {
  w = w || window;
  if (w.pageXOffset != null) return {x: w.pageXOffset, y:w.pageYOffset};
  var d = w.document;
  if (document.compatMode == "CSS1Compat") 
    return {x:d.documentElement.scrollLeft, y:d.documentElement.scrollTop};
  return { x: d.body.scrollLeft, y: d.body.scrollTop };
}

function zDrag(elementToDrag, event) { var scroll = getScrollOffsets();
  var startX = event.clientX + scroll.x;
  var startY = event.clientY + scroll.y;
  var origX = elementToDrag.offsetLeft;
  var origY = elementToDrag.offsetTop;
  var deltaX = startX - origX;
  var deltaY = startY - origY;
  if (document.addEventListener) { 
    document.addEventListener("mousemove", moveHandler, true);
    document.addEventListener("mouseup", upHandler, true);
  } 
  else if (document.attachEvent) { 
    elementToDrag.setCapture();
    elementToDrag.attachEvent("onmousemove", moveHandler);
    elementToDrag.attachEvent("onmouseup", upHandler);
    elementToDrag.attachEvent("onlosecapture", upHandler);
  } 
  if (event.stopPropagation) event.stopPropagation();
  else event.cancelBubble = true;
  if (event.preventDefault) event.preventDefault();
  else event.returnValue = false;

  function moveHandler(e) { 
    if (!e) e = window.event;
    var scroll = getScrollOffsets();
    elementToDrag.style.left = getLeft(e.clientX + scroll.x - deltaX,true) + "px";
    elementToDrag.style.top = getTop(e.clientY + scroll.y - deltaY,true) + "px";
    if (e.stopPropagation) e.stopPropagation();
    else e.cancelBubble = true;
  };
  function upHandler(e) { 
    if (!e) e = window.event;
    if (document.removeEventListener) { 
      document.removeEventListener("mouseup", upHandler, true);
      document.removeEventListener("mousemove", moveHandler, true);
    } 
    else if (document.detachEvent) { 
      elementToDrag.detachEvent("onlosecapture", upHandler);
      elementToDrag.detachEvent("onmouseup", upHandler);
      elementToDrag.detachEvent("onmousemove", moveHandler);
      elementToDrag.releaseCapture();
    } 
    if (e.stopPropagation) e.stopPropagation();
    else e.cancelBubble = true;
   };
 } 
  // end drag code

Ответы [ 2 ]

3 голосов
/ 16 декабря 2011

Это должно решить проблему липкости.

Я видел по вашему демонстрационному сайту, что когда мышь находится над внутренним фреймом, у нас возникает проблема с липкостью.Это связано с тем, что внутренний iframe не накапливает событие до корневого элемента документа, который обрабатывает событие mouseDove в zDrag.

Я решил эту проблему, добавив оверлей div (невидимый, но есть), который занимает всю областькорневой документ, тем самым эффективно предотвращая перемещение мышью по внутреннему фрейму.И поскольку этот оверлейный элемент div является прямым потомком нашего корневого элемента документа, он корректно отображает событие mousemove.

Изменения включают в себя дополнительный метод получения высоты документа (от http://james.padolsey.com/javascript/get-document-height-cross-browser/), и изменения наметод zDrag для добавления / отображения / скрытия элемента наложения.

function getDocHeight() {
  var D = document;
  return Math.max(
    Math.max(D.body.scrollHeight, D.documentElement.scrollHeight),
    Math.max(D.body.offsetHeight, D.documentElement.offsetHeight),
    Math.max(D.body.clientHeight, D.documentElement.clientHeight)
  );
}

function zDrag(elementToDrag, event) { 
  var scroll = getScrollOffsets();

  // create/show overlay - over the inner iframe and everything else
  var div = document.getElementById('overlay');
  if(div==null) {
    div = document.createElement('div');
    div.id = 'overlay';
    div.style.position = 'absolute';
    div.style.left = '0px';
    div.style.top = '0px';
    div.style.width = '100%';
    div.style.height = getDocHeight()+'px';
    div.style.zIndex = 99999;
    div.style.cursor = 'move';
    var bodyTag = document.getElementsByTagName("body")[0];
    bodyTag.appendChild(div);
  }
  else {
    div.style.display = 'block';
  }

  var startX = event.clientX + scroll.x;
  var startY = event.clientY + scroll.y;
  var origX = elementToDrag.offsetLeft;
  var origY = elementToDrag.offsetTop;
  var deltaX = startX - origX;
  var deltaY = startY - origY;
  if (document.addEventListener) { 
    document.addEventListener("mousemove", moveHandler, true);
    document.addEventListener("mouseup", upHandler, true);
  } 
  else if (document.attachEvent) { 
    /*elementToDrag.setCapture();
    elementToDrag.attachEvent("onmousemove", moveHandler);
    elementToDrag.attachEvent("onmouseup", upHandler);
    elementToDrag.attachEvent("onlosecapture", upHandler);*/

    // attach the events to the document element, to ensure we dont 'miss' any move events.
    document.setCapture();
    document.attachEvent("onmousemove", moveHandler);
    document.attachEvent("onmouseup", upHandler);
    document.attachEvent("onlosecapture", upHandler);
  } 

  if (event.stopPropagation) event.stopPropagation();
  else event.cancelBubble = true;
  if (event.preventDefault) event.preventDefault();
  else event.returnValue = false;

  function moveHandler(e) { 
    if (!e) e = window.event;
    var scroll = getScrollOffsets();
    elementToDrag.style.left = getLeft(e.clientX + scroll.x - deltaX,true) + "px";
    elementToDrag.style.top = getTop(e.clientY + scroll.y - deltaY,true) + "px";
    if (e.stopPropagation) e.stopPropagation();
    else e.cancelBubble = true;
  };

  function upHandler(e) { 

    // dragging is over. hide the overlay.
    document.getElementById('overlay').style.display = 'none';

    if (!e) e = window.event;
    if (document.removeEventListener) { 
      document.removeEventListener("mouseup", upHandler, true);
      document.removeEventListener("mousemove", moveHandler, true);
    } 
    else if (document.detachEvent) { 
      /*elementToDrag.detachEvent("onlosecapture", upHandler);
      elementToDrag.detachEvent("onmouseup", upHandler);
      elementToDrag.detachEvent("onmousemove", moveHandler);
      elementToDrag.releaseCapture();*/
      document.detachEvent("onlosecapture", upHandler);
      document.detachEvent("onmouseup", upHandler);
      document.detachEvent("onmousemove", moveHandler);
      document.releaseCapture();

    } 
    if (e.stopPropagation) e.stopPropagation();
    else e.cancelBubble = true;
  };
}

EDIT - для ограничения перетаскивания внутри области просмотра

Теперь он будет ограничивать перетаскивание довнутри окна просмотра (т. е. внутренней ширины и высоты окна браузера). Изменения включают в себя: a) дополнительную функцию кросс-браузера, позволяющую получить внутреннюю ширину и высоту окна (от http://www.javascripter.net/faq/browserw.htm) и b), изменения метода moveHandler () (внутри метода zDrag) для проверки и применения ограничений.

function getWindowSize() {
  var winW = 630, winH = 460;
  if (document.body && document.body.offsetWidth) {
    winW = document.body.offsetWidth;
    winH = document.body.offsetHeight;
  }
  if (document.compatMode=='CSS1Compat' && document.documentElement && document.documentElement.offsetWidth ) {
    winW = document.documentElement.offsetWidth;
    winH = document.documentElement.offsetHeight;
  }
  if (window.innerWidth && window.innerHeight) {
    winW = window.innerWidth;
    winH = window.innerHeight;
  }
  return {width: winW, height: winH};
}

А внутри zDrag () замените текущий moveHandler на:

function moveHandler(e) { 
  if (!e) e = window.event;
  var scroll = getScrollOffsets();

  var newLeft = getLeft(e.clientX + scroll.x - deltaX,true);
  if(newLeft + elementToDrag.offsetWidth > winDim.width) {
    newLeft = winDim.width - elementToDrag.offsetWidth;
  }
  elementToDrag.style.left = newLeft + "px";

  var newTop = getTop(e.clientY + scroll.y - deltaY,true);
  if(newTop + elementToDrag.offsetHeight > winDim.height) {
    newTop = winDim.height - elementToDrag.offsetHeight;
  }
  elementToDrag.style.top = newTop + "px";

  if (e.stopPropagation) e.stopPropagation();
  else e.cancelBubble = true;
};

EDIT - переменная winDim

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

var winDim = null;

function zDrag(...) {

  if(winDim == null) winDim = getWindowSize

  // ... rest of the code in zDrag ...

}
0 голосов
/ 16 декабря 2011

Основная проблема - iframe. Когда указатель мыши входит, вы прощаетесь со всеми зарегистрированными вами событиями. Замените iframe на div, и в Firefox все должно улучшиться. IE, кажется, просто обновляет дисплей быстрее; указатель мыши никогда не получает возможность ввести iframe в IE.

...