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

发布于 2024-12-21 11:56:46 字数 3321 浏览 4 评论 0 原文

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

我注意到,我可以在按下按键期间将指针移离拖动栏,并且弹出窗口可能会也可能不会跟随指针,或者突然捕捉到指针。

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

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

问题:如何使用纯 JS 提高 mousedown/drag 的粘性,以便窗口在 onmousedown 和 onmousemove 上保持附加状态?

另外请帮助我使 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

Using a plain JS script from David Flanagan to make my bookmarklet draggable.

I have noticed that I can move the pointer off the drag bar during key-down and the pop-up may or may not follow the pointer around or suddenly snap to the pointer.

The total experience in Firefox 8 and 9 is not impressive. IE8 on XP works as designed

It is intended for a bookmarklet, so I canot use a framework like jQuery or YUI.

QUESTION: How do I improve the stickyness of the mousedown / drag so the window stays attached to the mouse onmousedown and onmousemove using plain JS?

Also please help me make the getLeft and getTop work in IE/Chrome and Fx so the popup is restricted to the viewport.

OLD DEMO HERE

NEW AND FIXED DEMO HERE (thanks 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

好久不见√ 2024-12-28 11:56:46

这应该可以解决粘性问题。

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

我通过附加一个覆盖 div(不可见但在那里)来解决这个问题,该 div 占用根文档的整个区域,从而有效地防止内部 iframe 获取鼠标移动。由于此覆盖 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 ...

}

This should take care of the stickiness issue.

I could see from your demo site that when the mouse is over the inner iframe, we have an issue with stickiness. This is because the inner iframe does not bubble the event up to the root document element which handles zDrag's mousemove event.

I solved that issue by appending an overlay div (invisible but there) that takes the entire area of the root document, thereby effectively preventing the inner iframe from getting the mousemove. And as this overlay div is a direct descendant of our root document element, it bubbles up the mousemove event correctly.

Changes include an additional method for getting the document height (from http://james.padolsey.com/javascript/get-document-height-cross-browser/), and changes to the zDrag method for adding/showing/hiding the overlay div.

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 - For limiting the drag inside the viewport

Now it will limit the dragging to inside the viewport (i.e browser window inner width and height). Changes include a) an additional cross-browser function to get the window inner width and height (from http://www.javascripter.net/faq/browserw.htm) and b) changes to the moveHandler() method (inside the zDrag method) to check and enforce limits.

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

And inside zDrag(), replace the current moveHandler with:

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 - the winDim variable

winDim is the variable that stores the viewport's dimensions. It is used in the move handler to check if our movable is within the viewport. I kept it outside so as to avoid re-computation of window dimensions on every move event which could degrade performance.

var winDim = null;

function zDrag(...) {

  if(winDim == null) winDim = getWindowSize

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

}
半透明的墙 2024-12-28 11:56:46

主要问题是 iframe。当鼠标指针进入时,您就告别了所有注册的事件。用 div 替换 iframe,Firefox 中的情况应该会有所改善。 IE 似乎只是更新显示速度更快;鼠标指针永远没有机会进入 IE 中的 iframe。

The main problem is the iframe. When the mouse pointer enters that you say goodbye to all your registered events. Replace the iframe with a div and things should improve in Firefox. IE simply seems to update the display faster; the mouse pointer never gets the chance to enter the iframe in IE.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文