I need my users to be able to move an element away from the mouse pointer by holding the left button down, and the element should move closer when the button is up. So far, I have this:
var divName = 'follow'; // div to follow mouse
// (must be position:absolute)
var offX = 0; // X distance from mouse
var offY = 0; // Y distance from mouse
function mouseX(evt) {
if (!evt) evt = window.event;
if (evt.pageX) return evt.pageX;
else if (evt.clientX) return evt.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
else return 0;
}
function mouseY(evt) {
if (!evt) evt = window.event;
if (evt.pageY) return evt.pageY;
else if (evt.clientY) return evt.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
else return 0;
}
function follow(evt) {
if (document.getElementById) {
var obj = document.getElementById(divName).style;
obj.visibility = 'visible';
obj.left = (parseInt(mouseX(evt)) + offX) + 'px';
obj.top = (parseInt(mouseY(evt)) + offY) + 'px';
}
}
document.onmousemove = follow;
function discharge() { //Move away
offX += 1;
offY += 1;
}
function pull() { //Come closer
if (offX > 0) {
offX -= 1;
}
if (offY > 0) {
offY -= 1;
}
}
document.onmousedown = discharge;
document.onmouseup = pull;
offX and offY are the distance the element is from the mouse. In addition, this is just one part of the script. offX and offY come into play in a different part, which works except for this push/pull.
EDIT: Updated to include whole file and here is a fiddle.
More info: My main goal was to have an image within a div follow the mouse and move closer/further depending on the mouse's state. If anyone has a different way to achieve this, it would be greatly appreciated as well.
What you want requires two offsets, those of the mouse and those of the element.
var offX = 100; // mouse X-axis
var offY = 50; // mouse Y-axis
var elemOffX = 950; // elem X-axis
var elemOffY = 600; // elem Y-axis
// Now get the distance from the elem to the mouse:
var diffX = offX - elemOffX; // get the diff X
var diffY = offY - elemOffY; // get the diff Y
// Depending on your interval you might want to change the factor,
// this will move it in steps of 10%
var newOffX = offX + (diffX * 0.1); // set this as new result for the element's X
var newOffY = offY + (diffY * 0.1); // set this as new result for the element's Y
Please note, I havent tested this for bugs, this is to show a principle. Might be that you need to change the newOff's + to a -
In you example you do this: offY-1, you need to change that to offY=offY-1 or offY-=1
Here's a complete example using JQuery:
var divName = 'follow'; // div to follow mouse
// (must be position:absolute)
var offX = 0; // X distance from mouse
var offY = 0; // Y distance from mouse
var mouseX;
var mouseY;
var mouseState = 0;
var _this = this;
function follow(evt) {
if(_this.mouseState === 1) {
discharge();
} else {
pull();
}
if (document.getElementById) {
var obj = document.getElementById(divName).style;
obj.visibility = 'visible';
obj.left = evt.pageX + _this.offX + 'px';
obj.top = evt.pageY + _this.offY + 'px';
}
}
document.onmousemove = follow;
function discharge() { //Move away
_this.offX += 1;
_this.offY += 1;
}
function pull() { //Come closer
if (_this.offX > 0) {
_this.offX -= 1;
}
if (_this.offY > 0) {
_this.offY -= 1;
}
}
$(document).mousedown(function(e){_this.mouseState = 1;});
$(document).mouseup(function(e){_this.mouseState = 0; });
Im struggling with seemingly a simple javascript exercise, writing a vanilla drag and drop. I think Im making a mistake with my 'addeventlisteners', here is the code:
var ele = document.getElementsByClassName ("target")[0];
var stateMouseDown = false;
//ele.onmousedown = eleMouseDown;
ele.addEventListener ("onmousedown" , eleMouseDown , false);
function eleMouseDown () {
stateMouseDown = true;
document.addEventListener ("onmousemove" , eleMouseMove , false);
}
function eleMouseMove (ev) {
do {
var pX = ev.pageX;
var pY = ev.pageY;
ele.style.left = pX + "px";
ele.style.top = pY + "px";
document.addEventListener ("onmouseup" , eleMouseUp , false);
} while (stateMouseDown === true);
}
function eleMouseUp () {
stateMouseDown = false;
document.removeEventListener ("onmousemove" , eleMouseMove , false);
document.removeEventListener ("onmouseup" , eleMouseUp , false);
}
Here's a jsfiddle with it working: http://jsfiddle.net/fpb7j/1/
There were 2 main issues, first being the use of onmousedown, onmousemove and onmouseup. I believe those are only to be used with attached events:
document.body.attachEvent('onmousemove',drag);
while mousedown, mousemove and mouseup are for event listeners:
document.body.addEventListener('mousemove',drag);
The second issue was the do-while loop in the move event function. That function's being called every time the mouse moves a pixel, so the loop isn't needed:
var ele = document.getElementsByClassName ("target")[0];
ele.addEventListener ("mousedown" , eleMouseDown , false);
function eleMouseDown () {
stateMouseDown = true;
document.addEventListener ("mousemove" , eleMouseMove , false);
}
function eleMouseMove (ev) {
var pX = ev.pageX;
var pY = ev.pageY;
ele.style.left = pX + "px";
ele.style.top = pY + "px";
document.addEventListener ("mouseup" , eleMouseUp , false);
}
function eleMouseUp () {
document.removeEventListener ("mousemove" , eleMouseMove , false);
document.removeEventListener ("mouseup" , eleMouseUp , false);
}
By the way, I had to make the target's position absolute for it to work.
you can try this fiddle too, http://jsfiddle.net/dennisbot/4AH5Z/
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>titulo de mi pagina</title>
<style>
#target {
width: 100px;
height: 100px;
background-color: #ffc;
border: 2px solid blue;
position: absolute;
}
</style>
<script>
window.onload = function() {
var el = document.getElementById('target');
var mover = false, x, y, posx, posy, first = true;
el.onmousedown = function() {
mover = true;
};
el.onmouseup = function() {
mover = false;
first = true;
};
el.onmousemove = function(e) {
if (mover) {
if (first) {
x = e.offsetX;
y = e.offsetY;
first = false;
}
posx = e.pageX - x;
posy = e.pageY - y;
this.style.left = posx + 'px';
this.style.top = posy + 'px';
}
};
}
</script>
</head>
<body>
<div id="target" style="left: 10px; top:20px"></div>
</body>
</html>
I've just made a simple drag.
It's a one liner usage, and it handles things like the offset of the mouse to the top left corner of the element, onDrag/onStop callbacks, and SVG elements dragging
Here is the code.
// simple drag
function sdrag(onDrag, onStop) {
var startX = 0;
var startY = 0;
var el = this;
var dragging = false;
function move(e) {
el.style.left = (e.pageX - startX ) + 'px';
el.style.top = (e.pageY - startY ) + 'px';
onDrag && onDrag(el, e.pageX, startX, e.pageY, startY);
}
function startDragging(e) {
if (e.currentTarget instanceof HTMLElement || e.currentTarget instanceof SVGElement) {
dragging = true;
var left = el.style.left ? parseInt(el.style.left) : 0;
var top = el.style.top ? parseInt(el.style.top) : 0;
startX = e.pageX - left;
startY = e.pageY - top;
window.addEventListener('mousemove', move);
}
else {
throw new Error("Your target must be an html element");
}
}
this.addEventListener('mousedown', startDragging);
window.addEventListener('mouseup', function (e) {
if (true === dragging) {
dragging = false;
window.removeEventListener('mousemove', move);
onStop && onStop(el, e.pageX, startX, e.pageY, startY);
}
});
}
Element.prototype.sdrag = sdrag;
and to use it:
document.getElementById('my_target').sdrag();
You can also use onDrag and onStop callbacks:
document.getElementById('my_target').sdrag(onDrag, onStop);
Check my repo here for more details:
https://github.com/lingtalfi/simpledrag
this is how I do it
var MOVE = {
startX: undefined,
startY: undefined,
item: null
};
function contentDiv(color, width, height) {
var result = document.createElement('div');
result.style.width = width + 'px';
result.style.height = height + 'px';
result.style.backgroundColor = color;
return result;
}
function movable(content) {
var outer = document.createElement('div');
var inner = document.createElement('div');
outer.style.position = 'relative';
inner.style.position = 'relative';
inner.style.cursor = 'move';
inner.style.zIndex = 1000;
outer.appendChild(inner);
inner.appendChild(content);
inner.addEventListener('mousedown', function(evt) {
MOVE.item = this;
MOVE.startX = evt.pageX;
MOVE.startY = evt.pageY;
})
return outer;
}
function bodyOnload() {
document.getElementById('td1').appendChild(movable(contentDiv('blue', 100, 100)));
document.getElementById('td2').appendChild(movable(contentDiv('red', 100, 100)));
document.addEventListener('mousemove', function(evt) {
if (!MOVE.item) return;
if (evt.which!==1){ return; }
var dx = evt.pageX - MOVE.startX;
var dy = evt.pageY - MOVE.startY;
MOVE.item.parentElement.style.left = dx + 'px';
MOVE.item.parentElement.style.top = dy + 'px';
});
document.addEventListener('mouseup', function(evt) {
if (!MOVE.item) return;
var dx = evt.pageX - MOVE.startX;
var dy = evt.pageY - MOVE.startY;
var sty = MOVE.item.style;
sty.left = (parseFloat(sty.left) || 0) + dx + 'px';
sty.top = (parseFloat(sty.top) || 0) + dy + 'px';
MOVE.item.parentElement.style.left = '';
MOVE.item.parentElement.style.top = '';
MOVE.item = null;
MOVE.startX = undefined;
MOVE.startY = undefined;
});
}
bodyOnload();
table {
user-select: none
}
<table>
<tr>
<td id='td1'></td>
<td id='td2'></td>
</tr>
</table>
While dragging, the left and right of the style of the parentElement of the dragged element are continuously updated. Then, on mouseup (='drop'), "the changes are committed", so to speak; we add the (horizontal and vertical) position changes (i.e., left and top) of the parent to the position of the element itself, and we clear left/top of the parent again. This way, we only need JavaScript variables for pageX, pageY (mouse position at drag start), while concerning the element position at drag start, we don't need JavaScript variables for that (just keeping that information in the DOM).
If you're dealing with SVG elements, you can use the same parent/child/commit technique. Just use two nested g, and use transform=translate(dx,dy) instead of style.left=dx, style.top=dy
Following is my Code for reference.
One main image is display on Canvas.
On that image with dataJSON object I am passing x and y
co-ordinates and drawing ball images there.
Then tooltip is added. (function tooltipFunc)
But when I am panning image then tooltip is not visible on those
balls images on hover.**
In tooltipFunc function, if code is for default position when
page loads first time. And else code is for translate positions
when we are panning the image to show the tooltip.
But after panning, when mousehover on balls images then tooltip is
not visible. When I click on those ball images then tooltip is
visible but not on the translate position.
Any suggestions?
//Following is Code for reference
var isDown = false;
var startCoords = [];
var last = [0, 0];
canvas.onmousedown = function(e){
isDown = true;
startCoords = [
e.offsetX - last[0],
e.offsetY - last[1]
];
};
canvas.onmouseup = function(e){
isDown = false;
last = [
e.offsetX - startCoords[0], // set last coordinates
e.offsetY - startCoords[1]
];
//tooltipFunc(e);
};
canvas.onmousemove = function(e){
tooltipFunc(e); //tooltip function
if(!isDown) return;
var x = e.offsetX;
var y = e.offsetY;
context.setTransform(1, 0, 0, 1, x - startCoords[0], y - startCoords[1]);
draw1(scaleValue);//redrawing image on canvas while panning image
}
function tooltipFunc(e){
var translationX, translationY;
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top,
i = 0,
r,
inTooltip = false;
if((typeof startCoords[0] === 'undefined' || startCoords[0] === 'NaN') && (typeof startCoords[1] === 'undefined' || startCoords[1] === 'NaN')){
for (; r = dataJSON[i]; i++) {
if (x >= parseInt(dataJSON[i].x[0] * scaleValue) + parseInt(scaleValue) && x <= parseInt(dataJSON[i].x[0] * scaleValue) + parseInt(20/scaleValue) && y >= parseInt(dataJSON[i].y[0] * scaleValue) && y <= parseInt(dataJSON[i].y[0] * scaleValue) + parseInt(20/scaleValue)) {
clearTooltip();
showTooltip(e.clientX, e.clientY, i);
inTooltip = true;
}
}
}
else{
for (; r = dataJSON[i]; i++) {
translationX = parseInt(x) - parseInt(startCoords[0]);
translationY = parseInt(y) - parseInt(startCoords[1]);
if (x >= parseInt(dataJSON[i].x[0] * scaleValue) + parseInt(translationX) + parseInt(scaleValue) && x <= parseInt(dataJSON[i].x[0] * scaleValue) + parseInt(translationX) + parseInt(20/scaleValue) && y >= parseInt(dataJSON[i].y[0] * scaleValue) + parseInt(translationY) && y <= parseInt(dataJSON[i].y[0] * scaleValue) + parseInt(translationY) + parseInt(20/scaleValue)) {
clearTooltip();
var newX = e.clientX - translationX;
var newY = e.clientY - translationY
showTooltip(newX, newY, i);
inTooltip = true;
}
}
}
}
I have this code for DnD an element. It works for an especific element. However I wanna make it work for all divs. I can't reference "this.id" to the move function. I don't what is missing to make it work.
window.addEventListener('load', init, false);
function init(){
//just one element
/* box = document.getElementById('div1');
box.addEventListener('mousedown', startMoving, false);
box.addEventListener('mouseup', stopMoving, false);*/
//all element?
box = document.getElementsByTagName('div');
for (i=0;i<box.length;i++) {
box[i].addEventListener('mousedown', startMoving, false);
box[i].addEventListener('mouseup', stopMoving, false);
}
}
function startMoving(evt){
evt = evt || window.event;
var posX = evt.clientX,
posY = evt.clientY,
a = document.getElementById(this.id);
divTop = parseInt(a.style.top),
divLeft = parseInt(a.style.left);
var diffX = posX - divLeft,
diffY = posY - divTop;
document.onmousemove = function(evt){
evt = evt || window.event;
var posX = evt.clientX,
posY = evt.clientY,
aX = posX - diffX,
aY = posY - diffY;
move(this.id,aX,aY);
}
}
function stopMoving(){
document.onmousemove = function(){}
}
function move(divid,newX,newY){
var a = document.getElementById(divid);
a.style.left = newX + 'px';
a.style.top = newY + 'px';
}
Is there a better way to make this?
I want to modify the below function to be able to not just have drag bounds of the browser window, but also "snap" to it when the drag is 15px away from the bounds.
Zopim has this in their chat widget, see: http://zopim.com
(Click on the chat icon on the bottom right corner and start dragging the widget to the end of your browser window)
My current function is here: http://jsfiddle.net/N6A4q/
Code:
function enableDragging(ele) {
var dragging = dragging || false,
x, y, Ox, Oy,
current;
enableDragging.z = enableDragging.z || 1;
var grabber = ele;
grabber.onmousedown = function (ev) {
ev = ev || window.event;
var target = ev.target || ev.srcElement;
current = target.parentNode;
dragging = true;
x = ev.clientX ;
y = ev.clientY ;
Ox = current.offsetLeft;
Oy = current.offsetTop;
current.style.zIndex = ++enableDragging.z;
// cached value
var viewportWidth = viewport().width;
var viewportHeight = viewport().height;
document.onmousemove = function (ev) {
ev = ev || window.event;
if (dragging == true) {
var Sx = parseFloat(ev.clientX) - x + Ox;
var Sy = parseFloat(ev.clientY) - y + Oy;
current.style.left = Math.min(Math.max(Sx, Math.min(viewportWidth - Sx, 0)), viewportWidth - current.offsetWidth) + "px";
current.style.top = Math.min(Math.max(Sy, Math.min(viewportHeight - Sy, 0)), viewportHeight - current.offsetHeight) + "px";
}
}
document.onselectstart = function () {
return false;
};
document.onmouseup = function (ev) {
ev = ev || window.event;
dragging && (dragging = false);
if (ev.preventDefault) {
ev.preventDefault();
}
}
document.body.style.MozUserSelect = "none";
document.body.style.cursor = "default";
return false;
};
}
function viewport() {
var e = window
, a = 'inner';
if ( !( 'innerWidth' in window ) ) {
a = 'client';
e = document.documentElement || document.body;
}
return { width : e[ a+'Width' ] , height : e[ a+'Height' ] }
}
How could i make this snap the the bounds of the window in the same way?
Any ideas?