Im new to mobile touch events.
Im trying to make this code work on mobile too, but im afraid that im just wasting my time. I would not like to add some sort of library, just vanilla javascript.
Its a roadmap that i move inside div.
Any hints please?
Best regards,
Christian
function startDrag(e) {
// determine event object
if (!e) {
var e = window.event;
}
if(e.preventDefault) e.preventDefault();
// IE uses srcElement, others use target
targ = e.target ? e.target : e.srcElement;
if (targ.className != 'roadmap') {return};
// calculate event X, Y coordinates
offsetX = e.clientX;
offsetY = e.clientY;
// assign default values for top and left properties
if (!targ.style.left) { targ.style.left='-140px'};
if (!targ.style.top) { targ.style.top='-300px'};
// calculate integer values for top and left
// properties
coordX = parseInt(targ.style.left);
coordY = parseInt(targ.style.top);
drag = true;
// move div element
document.ontouchmove=dragDiv;
return false;
}
function dragDiv(e) {
if (!drag) {return};
if (!e) { var e= window.event};
// target
console.log(e.target)
var t = e.target,
img = t,
parent = img.parentElement,
imgWidth = img.clientWidth,
imgHeight = img.clientHeight;
// maxes
var y = coordY+e.clientY-offsetY,
x = coordX+e.clientX-offsetX;
// set boundies
if ( parent.clientHeight == null ) {
parent.clientHeight = 1;
targ.style.left=1+'px';
}
var imgBottom = parent.clientHeight-imgHeight
imgRight = parent.clientWidth-imgWidth;
// stop drag on overflow
if ( // left
/^-\d+$/.test(y) &&
// // top
/^-\d+$/.test(x) &&
// // bottom
imgBottom < y &&
// // bottom
imgRight < x
) {
targ.style.left=coordX+e.clientX-offsetX+'px';
targ.style.top=coordY+e.clientY-offsetY+'px';
};
return false;
}
function stopDrag() {
drag=false;
}
window.onload = function() {
document.onmousedown = startDrag;
document.onmouseup = stopDrag;
// mobile
document.addEventListener("touchmove", startDrag, false);
document.addEventListener("touchend", stopDrag, false);
}
Try using the 'touchstart' event instead of 'touchmove':
document.addEventListener("touchstart", startDrag, false);
document.addEventListener("touchend", stopDrag, false);
Then set your 'offsetX' and 'offsetY' variables to use either the mouse or touch coordinates:
offsetX = e.clientX || e.touches[0].clientX;
offsetY = e.clientY || e.touches[0].clientY;
Hope this helps.
Related
Good day,
Learning Javascript and trying to make draggable elements inside a container.
How to set the draggable border so that elements won't be able to move outside it ?
Right now i have a problem when you drag something to the bottom or right border the element moves outside the container.
fiddle
my HTML looks like this :
<div id="container">
<div id="comboCon1"></div>
<div id="comboCon2"></div>
</div>
Here is the function where i get all positions and call the onmousemove Event :
function OnMouseClickDown(event) {
var target; // -> Element that triggered the event
if (event.target != null) { // -> If Browser is IE than use 'srcElement'
target = event.target;
} else {
target = event.srcElement;
}
// Check which button was clicked and if element has class 'draggable'
if ((event.button == 1 || event.button == 0) && target.className == "draggable") {
// Current Mouse position
startX = event.clientX;
startY = event.clientY;
// Current Element position
offsetX = ExtractNumber(target.style.left); // -> Convert to INT
offsetY = ExtractNumber(target.style.top);
// Border ( Div Container )
minBoundX = target.parentNode.offsetLeft; // Minimal -> Top Position.
minBoundY = target.parentNode.offsetTop;
maxBoundX = minBoundX + target.parentNode.offsetWidth - target.offsetWidth; // Maximal.
maxBoundY = minBoundY + target.parentNode.offsetHeight - target.offsetHeight;
oldZIndex = target.style.zIndex;
target.style.zIndex = 10; // -> Move element infront of others
dragElement = target; // -> Pass to onMouseMove
document.onmousemove = OnMouseMove; // -> Begin drag.
document.body.focus() // -> Cancel selections
document.onselectstart = function () { return false }; // -> Cancel selection in IE.
}
}
And here is onmousemove Event :
function OnMouseMove(event) {
dragElement.style.left = Math.max(minBoundX, Math.min(offsetX + event.clientX - startX, maxBoundX)) + "px";
dragElement.style.top = Math.max(minBoundY, Math.min(offsetY + event.clientY - startY, maxBoundY)) + "px";
}
there is a little change in css for solve this problem, because you are usingf position "relative" the offset of container is given to the child is draged
so in my demo put drag element in position absolute, and change offsetWidth for clientWidth and seems works ( horizontal):
// Draggable Div 1
document.getElementById("comboCon1").style.position = "relative"; // -> Add position relative
document.getElementById("comboCon1").style.width = "151px";
document.getElementById("comboCon1").style.height = "10px";
document.getElementById("comboCon1").setAttribute("class", "draggable");
document.getElementById("comboCon1").style.border = "1px solid black";
document.getElementById("comboCon1").style.padding = "0px";
// Draggable Div 2
document.getElementById("comboCon2").style.position = "relative";
document.getElementById("comboCon2").style.width = "151px";
document.getElementById("comboCon2").setAttribute("class", "draggable");
document.getElementById("comboCon2").style.border = "1px solid black";
document.getElementById("comboCon2").style.padding = "10px";
// Container
document.getElementById("container").style.border = "1px solid black";
document.getElementById("container").style.width = "500px";
document.getElementById("container").style.height = "500px";
//////////////////////
// Begin Drag events
//////////////////////
var startX = 0; //-> Mouse position.
var startY = 0;
var offsetX = 0; // -> Element position
var offsetY = 0;
var minBoundX = 0; // -> Top Drag Position ( Minimum )
var minBoundY = 0;
var maxBoundX = 0; // -> Bottom Drag Position ( Maximum )
var maxBoundY = 0;
var dragElement; // -> Pass the target to OnMouseMove Event
var oldZIndex = 0; // -> Increase Z-Index while drag
// 1)
initDragDrop(); // -> initialize 2 Events.
function initDragDrop() {
document.onmousedown = OnMouseClickDown;
document.onmouseup = OnMouseClickUp;
}
// 2) Click on Element
function OnMouseClickDown(event) {
var target; // -> Element that triggered the event
if (event.target != null) { // -> If Browser is IE than use 'srcElement'
target = event.target;
} else {
target = event.srcElement;
}
// Check which button was clicked and if element has class 'draggable'
if ((event.button == 1 || event.button == 0) && target.className == "draggable") {
// Current Mouse position
startX = event.clientX;
startY = event.clientY;
// Current Element position
offsetX = ExtractNumber(target.style.left); // -> Convert to INT
offsetY = ExtractNumber(target.style.top);
// Border ( Div Container )
minBoundX = target.parentNode.offsetLeft; // Minimal -> Top Position.
console.log(target.parentNode.getBoundingClientRect(), target)
minBoundY = target.parentNode.offsetTop;
maxBoundX = minBoundX + target.parentNode.clientWidth - target.clientWidth; // Maximal.
console.log(maxBoundX, target.parentNode.clientWidth, target.clientWidth);
maxBoundY = minBoundY + target.parentNode.offsetHeight - target.offsetHeight;
oldZIndex = target.style.zIndex;
target.style.zIndex = 10; // -> Move element infront of others
target.style.position = 'absolute'
dragElement = target; // -> Pass to onMouseMove
document.onmousemove = OnMouseMove; // -> Begin drag.
document.body.focus() // -> Cancel selections
document.onselectstart = function () { return false }; // -> Cancel selection in IE.
}
}
// 3) Convert current Element position in INT
function ExtractNumber(value) {
var number = parseInt(value);
if (number == null || isNaN(number)) {
return 0;
}
else {
return number;
}
}
// 4) Drag
function OnMouseMove(event) {
dragElement.style.left = Math.max(minBoundX, Math.min(offsetX + event.clientX - startX, maxBoundX)) + "px";
dragElement.style.top = Math.max(minBoundY, Math.min(offsetY + event.clientY - startY, maxBoundY)) + "px";
}
// 5) Drop
function OnMouseClickUp(event) {
if (dragElement != null) {
dragElement.style.zIndex = oldZIndex; // -> set Z-index 0.
document.onmousemove = null;
document.onselectstart = null;
dragElement = null; // -> No more element to drag.
}
}
What I mean is that the user presses a mouse button at point xy on an HTML canvas and while the mouse button is pressed the rectangle can be resized according to the movement of the cursor with point xy fixed. Like how highlighting works.
This is what I've got so far but it doesn't seem to be working:
canvas.addEventListener('mousedown', function(e){
var rectx = e.clientX;
var recty = e.clientY;
canvas.onmousemove = function(e){
var df = e.clientX;
var fg = e.clientY;
};
context.rect(rectx, recty, df-rectx, fg-recty);
context.stroke();
}, false);
Assuming there are no transforms (scale, translate) on your canvas context.
Basic steps for having a resizable rectangle are as follows:
Create a mousedown listener that sets a flag indicating the use is holding down the mouse button, as well as sets the "anchor," or initial coordinates.
Create a mouseup listener that unsets the flag.
Create a mousemove listener that, if the flag indicates the mouse is down, redraws the canvas with the rectangle's size changed according to mouse coordinates.
An important note is that client coordinates in the event object are relative to the page, not to your canvas element. You will frequently need to convert clientX and clientY into canvas coordinates:
var getCanvasCoords = function (clientX, clientY) {
var rect = canvas.getBoundingClientRect();
return {
x: clientX - rect.left,
y: clientY - rect.top
};
};
The first two steps look something like this:
var anchorX;
var anchorY;
var mouseDown = false;
canvas.addEventListener('mousedown', function (event) {
var coords = getCanvasCoords(event.clientX, event.clientY);
anchorX = coords.x;
anchorY = coords.y;
mouseDown = true;
});
canvas.addEventListener('mouseup', function (event) {
mouseDown = false;
});
And the mousemove handler:
canvas.addEventListener('mousemove', function (event) {
var coords = getCanvasCoords(event.clientX, event.clientY);
var width = coords.x - anchorX;
var height = coords.y - anchorY;
// clear canvas for redrawing
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillRect(anchorX, anchorY, width, height);
});
Don't Render from mouse events!
The given answer is correct but it is not the best way to do this.
The are two reasons. First the mousemove event can fire up to 600+ times a second but the display only refreshes 60 times a second. Rendering from the input event is many time just a waste of CPU time as the results will be overwritten by the next mouse event before it is ever had a chance to be displayed.
The second reason is that dedicating an event listener to a single task makes it hard to add more functionality. You end up adding more and more code to the mousemove event to handle all the types of input, most of which can be ignored because of the high update speed of the mouse.
Mouse listeners
Mouse event listeners do the minimum possible. They just record the mouse state and no more. Also all mouse events return the mouse position. You should not ignore the mouse position for events like mouse down and up
The following function creates a mouse object for a element. The mouse object has the x,y position relative to the to left of the element, and the current button states for 3 buttons it is left to right button1, button2, button3.
Also when the mouse leaves the element and then releases the mouse button the mouse for the element will not see the mouseup event and not know the mouse button is up. To prevent the mouse buttons from getting stuck you turn off the buttons when the mouse leaves the element.
The best mouse listener is to the whole page as it can track mouse events that happen even when the mouse is outside the window/tab (if the window/tab has focus), but that is a little to complex for this answer.
Function to create a mouse for an element
function createMouse(element){
var mouse = {
x : 0,
y : 0,
button1 : false,
button2 : false,
button3 : false,
over : false,
};
function mouseEvent(event){
var bounds = element.getBoundingClientRect();
mouse.x = event.pageX - bounds.left - scrollX;
mouse.y = event.pageY - bounds.top - scrollY;
if(event.type === "mousedown"){
mouse["button"+event.which] = true;
} else if(event.type === "mouseup"){
mouse["button"+event.which] = false;
} else if(event.type === "mouseover"){
mouse.over = true;
} else if(event.type === "mouseout"){
mouse.over = false;
mouse.button1 = false; // turn of buttons to prevent them locking
mouse.button2 = false;
mouse.button3 = false;
}
event.preventDefault(); // stops default mouse behaviour.
}
var events = "mousemove,mousedown,mouseup,mouseout,mouseover".split(',');
events.forEach(eventType => element.addEventListener(eventType,mouseEvent));
mouse.remove = function(){
events.forEach(eventType => element.removeEventListener(eventType, mouseEvent));
}
return mouse;
}
Using the mouse
It is now just a matter of creating a mouse for the element
var canMouse = createMouse(canvas);
And then in your main render loop do the dragging.
var drag = {
x : 0,
y : 0,
x1 : 0,
y1 : 0,
dragging : false,
top : 0,
left : 0,
width : 0,
height : 0,
}
function mainLoop(){
if(canMouse.button1){ // is button down
if(!drag.dragging){ // is dragging
drag.x = canMouse.x;
drag.y = canMouse.y;
drag.dragging = true;
}
drag.x1 = canMouse.x;
drag.y1 = canMouse.y;
drag.top = Math.min(drag.y, drag.y1);
drag.left = Math.min(drag.x, drag.x1);
drag.width = Math.abs(drag.x - drag.x1);
drag.height = Math.abs(drag.y - drag.y1);
}else{
if(drag.dragging){
drag.dragging = false;
}
}
}
Putting it all together
function createMouse(element){
var mouse = {
x : 0,
y : 0,
button1 : false,
button2 : false,
button3 : false,
over : false,
};
function mouseEvent(event){
var bounds = element.getBoundingClientRect();
// NOTE getting the border should not be done like this as
// it will not work in all cases.
var border = Number(element.style.border.split("px")[0])
mouse.x = event.pageX - bounds.left - scrollX - border;
mouse.y = event.pageY - bounds.top - scrollY - border;
if(event.type === "mousedown"){
mouse["button"+event.which] = true;
} else if(event.type === "mouseup"){
mouse["button"+event.which] = false;
} else if(event.type === "mouseover"){
mouse.over = true;
} else if(event.type === "mouseout"){
mouse.over = false;
mouse.button1 = false; // turn of buttons to prevent them locking
mouse.button2 = false;
mouse.button3 = false;
}
event.preventDefault(); // stops default mouse behaviour.
}
var events = "mousemove,mousedown,mouseup,mouseout,mouseover".split(',');
events.forEach(eventType => element.addEventListener(eventType,mouseEvent));
mouse.remove = function(){
events.forEach(eventType => element.removeEventListener(eventType, mouseEvent));
}
return mouse;
}
var drag = {
x : 0,
y : 0,
x1 : 0,
y1 : 0,
dragging : false,
top : 0,
left : 0,
width : 0,
height : 0,
}
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
var mouse = createMouse(canvas);
function update(){
ctx.clearRect(0,0,canvas.width,canvas.height);
if(mouse.button1){ // is button down
if(!drag.dragging){ // is dragging
drag.x = mouse.x;
drag.y = mouse.y;
drag.dragging = true;
}
drag.x1 = mouse.x;
drag.y1 = mouse.y;
drag.top = Math.min(drag.y, drag.y1);
drag.left = Math.min(drag.x, drag.x1);
drag.width = Math.abs(drag.x - drag.x1);
drag.height = Math.abs(drag.y - drag.y1);
}else{
if(drag.dragging){
drag.dragging = false;
}
}
if(drag.dragging){
ctx.strokeRect(drag.left, drag.top, drag.width, drag.height);
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
border : 1px solid black;
}
Click drag to draw rectangle.<br>
<canvas id="canvas" width= "512" height = "256"></canvas>
I making a simple drag'n'drop interface. I have a bunch of containers ("wrapper") and some dynamically added items ("dragElement") in one of them. So I need, when I move item over another container, JS detect it and move the item there when the drag is finished.
I tried to detect container with "onmouseover" and "mouseup" when dragging item, but had no success, because, actually, mouse always was over the dragged element.
So how can I detect container when drag item? In pure JS please...
document.onmousedown = function(e) {
var dragElement = e.target;
if (!dragElement.classList.contains('draggable')) return;
var coords, shiftX, shiftY, detectPage;
startDrag(e.clientX, e.clientY);
document.onmousemove = function(e) {
moveAt(e.clientX, e.clientY);
};
wrapper.onmouseover = function(e) {
detectPage = e.target;
console.log(detectPage);
};
dragElement.onmouseup = function() {
finishDrag();
};
function startDrag(clientX, clientY) {
shiftX = clientX - dragElement.getBoundingClientRect().left;
shiftY = clientY - dragElement.getBoundingClientRect().top;
dragElement.style.position = 'fixed';
document.body.appendChild(dragElement);
moveAt(clientX, clientY);
};
function finishDrag() {
dragElement.style.top = parseInt(dragElement.style.top) - wrapper.getBoundingClientRect().top + 'px';
dragElement.style.position = 'absolute';
wrapper.onmouseup = function(e) {
var selectPage = e.target;
}
wrapper.appendChild(dragElement);
document.onmousemove = null;
dragElement.onmouseup = null;
};
function moveAt(clientX, clientY) {
var newX = clientX - shiftX;
var newY = clientY - shiftY;
if (newX < 0) newX = 0;
if (newX > wrapper.offsetWidth - dragElement.offsetWidth) {
newX = wrapper.offsetWidth - dragElement.offsetWidth;
}
dragElement.style.left = newX + 'px';
dragElement.style.top = newY + 'px';
};
return false;
};
Well, no one help. So one free day gone to find the solution. All I can found is to delete function finishDrag() from dragElement.onmouseup and change it to the code below.
If in shorter, when onmouseup comes, dragElement must go to display:none and now we can get access to the object near the mouse cursor through elementFromPoint. When we done with it, we can easily detects container, bring an element back to display:block and put it to that container...
Hope, it helps to someone...
dragElement.onmouseup = function(e) {
dragElement.style.display = 'none';
var selectPage = document.elementFromPoint(e.clientX, e.clientY);
dragElement.style.display = 'block';
dragElement.style.top = parseInt(dragElement.style.top) - selectPage.getBoundingClientRect().top + 'px';
dragElement.style.position = 'absolute';
selectPage.appendChild(dragElement);
document.onmousemove = null;
dragElement.onmouseup = null;
};
Im trying to make planets that give an alert message when clicked.
Problem is, onmousedown only works on canvas, as far I tested.
Code for planets:
var planets = [];
for (var b=0;b<3;b++) {
planets.push(planet(0,360,Math.random()*600,Math.random()*600));
}
function planet(I,shiips,xpos,ypos){
I = I||{};
I.ships = shiips;
I.x=xpos;
I.y=ypos;
return I;
}
code for click detection; tests both for planet object and the image
update = function(){
planetImage.onmousedown=function(){alert("works!")};
planets[0].onmousedown=function(){alert("works!")};
}
setInterval(update,100);
Im using canvas to draw the images, if that hhelps.
I found the following code that gives mouse position, but it doesnt work for me:
(function() {
var mousePos;
window.onmousemove = handleMouseMove;
setInterval(getMousePosition, 100); // setInterval repeats every X ms
function handleMouseMove(event) {
event = event || window.event; // IE-ism
mousePos = {
x: event.clientX,
y: event.clientY
};
}
function getMousePosition() {
var pos = mousePos;
if (!pos) {
// We haven't seen any movement yet
}
else {
// Use pos.x and pox.y
}
}
})();
Im trying to keep it simple, I don't really like jquery or anything complicated.
Once again: the problem is onmousedown only works on the canvas object, i.e.
canvas.onmousedown=function(){alert("works!")};
I got it working now with this code:
update = function(){
canvas.onmousedown=function(){
var e = window.event;
var posX = e.clientX;
var posY = e.clientY;
alert("X position: "+ posX + " Y position: " + posY);
};
setInterval(update,100);
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
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 ...
}
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.