为什么我可以在此脚本中将鼠标从拖动区域拉出来?

时间:2011-12-14 08:13:06

标签: javascript mouseevent drag gecko

使用David Flanagan的普通JS脚本使我的书签可拖动。

我注意到我可以在按键期间将指针从拖动条上移开,弹出窗口可能会或可能不会跟随指针或突然对齐指针。

Firefox 8和9的总体体验并不令人印象深刻。 XP上的IE8按设计工作

它适用于书签,所以我可以使用像jQuery或YUI这样的框架。

问题:如何改善mousedown / drag的粘性,以便窗口保持连接鼠标onMousedown和onmousemove使用普通JS?

另外请帮助我让getLeft和getTop在IE / Chrome和Fx中工作,这样弹出窗口就只限于视口。

OLD DEMO HERE

NEW AND FIXED DEMO HERE(谢谢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 个答案:

答案 0 :(得分:3)

这应该解决粘性问题。

我可以从您的演示网站上看到,当鼠标位于内部iframe上方时,我们遇到了粘性问题。这是因为内部iframe不会将事件冒泡到处理zDrag的mousemove事件的根文档元素。

我通过附加一个覆盖div(不可见但在那里)来解决这个问题,该div占用根文档的整个区域,从而有效地防止内部iframe获取mousemove。因为这个overlay div是我们的根文档元素的直接后代,所以它正确地冒充了mousemove事件。

更改包括获取文档高度的其他方法(来自http://james.padolsey.com/javascript/get-document-height-cross-browser/),以及更改为添加/显示/隐藏叠加div的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;
  };
}

编辑 - 用于限制视口内的拖动

现在它将限制拖动到视口内部(即浏览器窗口内部宽度和高度)。更改包括: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;
};

编辑 - winDim变量

winDim是存储视口尺寸的变量。它在移动处理程序中用于检查我们的移动是否在视口内。我将它保留在外面,以避免在每次移动事件中重新计算窗口尺寸,这可能会降低性能。

var winDim = null;

function zDrag(...) {

  if(winDim == null) winDim = getWindowSize

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

}

答案 1 :(得分:0)

主要问题是iframe。当鼠标指针进入时,您告别所有已注册的事件。用div替换iframe,Firefox中的内容应该有所改进。 IE似乎只是更快地更新显示;鼠标指针永远不会有机会在IE中输入iframe。