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.
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
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.
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.
And inside zDrag(), replace the current moveHandler with:
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.