I'm using a mousedown/mouseup solution for click and drag navigation but if hovering over a link and dragging it fires the link when the click is released.
How can I disable the click function when dragging?
Here's what I've got:
jQuery(document).ready(function($) {
$(".main-container").mousedown(function(e) {
e.preventDefault();
down = true;
x = e.pageX;
y = e.pageY;
top = $(this).scrollTop();
left = $(this).scrollLeft();
$(event.toElement).one('click', function(e) {
});
});
$("body").mousemove(function(e) {
if (down) {
var newX = e.pageX;
var newY = e.pageY;
//console.log(y+", "+newY+", "+top+", "+(top+(newY-y)));
$(".main-container").scrollTop(top - newY + y);
$(".main-container").scrollLeft(left - newX + x);
}
});
$("body").mouseup(function(e) {
down = false;
$(event.toElement).one('click', function(e) {
});
});
});
Thanks
This seems to do the trick:
jQuery(document).ready(function($) {
var x, y, top, left, down;
$(".main-container").mousedown(function(e) {
e.preventDefault();
down = true;
x = e.pageX;
y = e.pageY;
top = $(this).scrollTop();
left = $(this).scrollLeft();
timestamp = new Date().getTime();
});
$("body").mousemove(function(e) {
if (down) {
var newX = e.pageX;
var newY = e.pageY;
$(".main-container").scrollTop(top - newY + y);
$(".main-container").scrollLeft(left - newX + x);
}
});
$("body").mouseup(function(e) {
down = false;
});
$(".main-container").click(function(event) {
var interval = 350;
var timestamp2 = new Date().getTime();
if ((timestamp2 - timestamp) > interval) {
return false;
}
});
});
Related
I want to achieve 2 things on my page:
drag HTML elements on the screen;
zoom the page using the "wheel" event.
I can accomplish both of these as follows:
Element Drag:
function make_element_draggable(id) {
const elem = document.getElementById(id);
elem.style.position = "absolute";
let initX, initY, firstX, firstY, whichDown;
window.addEventListener("mouseup", function() {
if(whichDown) {
whichDown.style.zIndex = 0;
}
whichDown = null;
}, false);
window.addEventListener("mousemove", draggable, false);
elem.addEventListener("mousedown", function(e) {
e.preventDefault();
whichDown = this;
initX = this.offsetLeft;
initY = this.offsetTop;
firstX = e.pageX;
firstY = e.pageY;
});
function draggable(e) {
e.preventDefault();
if(!whichDown) return;
whichDown.style.zIndex = 9;
whichDown.style.left = initX + e.pageX - firstX + "px";
whichDown.style.top = initY + e.pageY - firstY + "px";
}
}
Page Zoom:
function page_zoom(container_id) {
zoom = 1;
zoom_speed = 0.1;
const container = document.getElementById(container_id);
document.addEventListener("wheel", function(e) {
if(e.deltaY > 0) {
container.style.transform = `scale(${zoom += zoom_speed})`;
} else {
container.style.transform = `scale(${zoom -= zoom_speed})`;
}
});
}
HTML:
<body id="body">
<div id="container">
<a id="text_1">TEXT 1</a>
</div>
</body>
Usage:
make_element_draggable("text_1")
page_zoom("body")
Here is the result.
Notice how the drag works perfectly when no zoom in enabled (text remains centered in the circle), but once zoom in enabled the text no longer maintains its position relative to the cursor.
Is there a way to compensate for zoom amount, perhaps using it as a factor to adjust the top and left settings of the drag function?
Here is a CODEPEN showing all the code. Drag the text element, then zoom using your mouse wheel (trackpad on Mac), and notice the incorrect positioning of the text element relative to the cursor.
You forgot to take in account zoom when dragging:
function make_element_draggable(id) {
const elem = document.getElementById(id);
elem.style.position = "absolute";
let initX, initY, firstX, firstY, whichDown;
window.addEventListener("mouseup", function() {
if(whichDown) {
whichDown.style.zIndex = 0;
}
whichDown = null;
}, false);
window.addEventListener("mousemove", draggable, false);
elem.addEventListener("mousedown", function(e) {
e.preventDefault();
whichDown = this;
initX = this.offsetLeft;
initY = this.offsetTop;
firstX = e.pageX;
firstY = e.pageY;
});
function draggable(e) {
e.preventDefault();
if(!whichDown) return;
whichDown.style.zIndex = 9;
whichDown.style.left = initX + (e.pageX - firstX)/zoom + "px";
whichDown.style.top = initY + (e.pageY - firstY)/zoom + "px";
}
}
function page_zoom(container_id) {
zoom = 1;
zoom_speed = 0.1;
const container = document.getElementById(container_id);
document.addEventListener("wheel", function(e) {
if(e.deltaY > 0) {
container.style.transform = `scale(${zoom += zoom_speed})`;
} else {
container.style.transform = `scale(${zoom -= zoom_speed})`;
}
});
}
page_zoom("body")
make_element_draggable("text_1")
<body id="body">
<div id="container">
<a id="text_1">TEXT 1</a>
</div>
</body>
I am using this W3school drag and drop API but i want it to drop on the specific position the cursor left of, instead it is always going to the top right. I have tried this code but i dont know if i am doing it right
var dragObj, count=0;
function set_drag_drop(obj)
{
if(count>0){
obj.adx = 10;
obj.ady = 10 + (count*100)
}else{
obj.adx = 0;
obj.ady = 0;
}
count++;
obj.onmousedown = function(e)
{
var rect = obj.getBoundingClientRect();
obj.dx = rect.left - e.clientX;
obj.dy = rect.top - e.clientY;
obj.isDown = true;
dragObj = this;
}
obj.onmouseup = function(e)
{
obj.isDown = false;
}
document.onmousemove = function(e)
{
if(dragObj && dragObj.isDown)
{
dragObj.style.left = e.pageX -dragObj.adx+ dragObj.dx +"px";
dragObj.style.top = e.pageY -dragObj.ady+ dragObj.dy + "px";
}
}
}
set_drag_drop(document.getElementById("obj1"));
check it out here
I've made a rotating menu.
To select an item you rotate the menu by clicking and dragging.
http://codepen.io/PaulBunker/pen/ZGgxvY
var dragging = false;
var links = $('.menu a');
var radius = 520;
var degree = 0.262;
var angle = 0.79;
var orgX;
var orgY;
var offset = $('.menu').offset();
var newangle;
var origradians;
function setItems(angle) {
var internalangle = angle;
links.each(function() {
var y = Math.round(radius * Math.cos(internalangle));
var x = Math.round(radius * Math.sin(internalangle));
$(this).css({
left: x + 'px',
top: 0-y + 'px',
display:'block',
});
$(this).addClass(y);
if (y < 10 & x > 0) {
$(this).addClass('active');
}
if ( y < -10 || y > 0) {
$(this).removeClass('active');
}
internalangle += degree;
});
}
$(function() {
$(document).mousedown(function(evt) {
orgX = evt.pageX - offset.left;
orgY = evt.pageY - offset.top;
orgradians = Math.atan2(orgX, orgY);
dragging = true;
});
$(document).mouseup(function() {
dragging = false;
angle = newangle;
});
$(document).mousemove(function(evt) {
if (dragging) {
var x = evt.pageX - offset.left;
var y = evt.pageY - offset.top;
var radians = Math.atan2(y, x);
newangle = (orgradians + radians) - angle;
console.log (orgradians , radians, angle, newangle);
setItems(newangle);
console.log(newangle);
}
});
setItems(angle);
});
My problem is after the first drag to select an item.
At the beginning of the second drag the menu jumps into the wrong position.
on 'mouseup' I save the 'newangle' variable and override the 'angle variable'.
I suspect there is an error somewhere in the line
newangle = (orgradians + radians) - angle;
I've been tearing my hair out to try and get this to work!
Thanks in advance for any guidance!
-Paul
If you don't use trigonometry on mouse move and just use it once when the radius of the circle is defined it'll fix the issue:
Work out the rotation speed based on the radius of the circle:
var rotationSpeed = Math.atan(1/radius);
Use the rotation speed and the difference in the Y position of the cursor on mouse move:
var newangle = lastangle - (difY*rotationSpeed);
http://codepen.io/stevenarcher/pen/yNmRyP
var dragging = false;
var links = $('.menu a');
var radius = 520;
var degree = 0.262;
var lastY;
var offset = $('.menu').offset();
var lastangle = 0;
var rotationSpeed = Math.atan(1/radius);
function setItems(internalangle) {
links.each(function() {
var y = Math.round(radius * Math.cos(internalangle));
var x = Math.round(radius * Math.sin(internalangle));
$(this).css({
left: x + 'px',
top: 0 - y + 'px',
display:'block',
});
$(this).addClass(y);
if (y < 10) {
$(this).addClass('active');
}
if ( y < -10 || y > 0) {
$(this).removeClass('active');
}
internalangle += degree;
});
}
$(document).mousedown(function(evt) {
lastY = evt.pageY - offset.top;
dragging = true;
});
$(document).mouseup(function(evt) {
dragging = false;
});
$(document).mousemove(function(evt) {
if (dragging) {
var y = evt.pageY - offset.top;
var difY = lastY - y;
lastY = y;
var newangle = lastangle - (difY*rotationSpeed);
setItems(newangle);
lastangle = newangle;
}
});
setItems(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
I'm creating a jQuery scrollbar which scrolls the content in a <div>. It's something like jQuery ScrollPane.
I've come to the point where I need to move the scroll button. My question is: what is the best way to do it without any UI plugin? So when the user clicks on the scroll button it can be moved vertically in its parent container with a mousedown event. How could I do that?
Here's a starting point, hope that's what you're after:
$(function() {
$('.slider').slider();
});
$.fn.slider = function() {
return this.each(function() {
var $el = $(this);
$el.css('top', 0);
var dragging = false;
var startY = 0;
var startT = 0;
$el.mousedown(function(ev) {
dragging = true;
startY = ev.clientY;
startT = $el.css('top');
});
$(window).mousemove(function(ev) {
if (dragging) {
// calculate new top
var newTop = parseInt(startT) + (ev.clientY - startY);
//stay in parent
var maxTop = $el.parent().height()-$el.height();
newTop = newTop<0?0:newTop>maxTop?maxTop:newTop;
$el.css('top', newTop );
}
}).mouseup(function() {
dragging = false;
});
});
}
.container{
position:relative;
border:1px solid red;
height:100px;
}
.slider{
height:20px;
width:20px;
background:green;
position:absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<div class="container">
<div class="slider" />
</div>
</div>
<br/>
<div class="container">
<div class="slider" />
</div>
I developed a right nav dock style item with just plain javascript.
Here is the link:
https://github.com/developerDoug/HtmlJavascriptDockInVS2010
Using a ui plugin would be best if you can convince your customer to do so. If not, what you'll need to focus on is hanling mousedown or something similar to that to be noticed that moving has began. The there methods to focus on are: mousedown, mousemove, mouseup.
For example, if on some div, you detect mousedown, you could call a function which basically would be your beginDrag, get x y coordinates, keep a reference to the start coordinates, attachEvent (if IE), addEventListener (for all other browsers).
ex:
// keep reference to drag div
_dragObj = new Object();
$("myDragDiv").mousedown(function(e) {
dragBegin(e);
}
function dragBegin(e) {
_dragObj = document.getElementById('myDragDiv');
var x, y;
if (navigator.userAgent.indexOf("MSIE") >= 0) {
x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
y = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop;
} else {
x = e.clientX + window.scrollX;
y = e.clientY + window.scrollY;
}
_dragObj.cursorStartX = x;
_dragObj.cursorStartY = y;
if (navigator.userAgent.indexOf("MSIE") >= 0) {
document.attachEvent("onmousemove", dragContinue);
document.attachEvent("onmouseup", dragEnd);
window.event.cancelBubble = true;
window.event.returnValue = false;
} else {
document.addEventListener("mousemove", dragContinue, true);
document.addEventListener("mouseup", dragEnd, true);
e.preventDefault();
}
}
function dragContinue(e) {
var x, y;
var isIE = _browser.isIE;
var isWebKitBased = _browser.isWebKitBased;
if (navigator.userAgent.indexOf("MSIE") >= 0) {
x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
y = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop;
} else {
x = e.clientX + window.scrollX;
y = e.clientY + window.scrollY;
}
var distance = x - _dragObj.cursorStartX;
distance = Math.abs(distance);
// or top, bottom, right
_dragObj.elNode.style.left = (_dragObj.elStartLeft + x - _dragObj.cursorStartX) + "px";
if (navigator.userAgent.indexOf("MSIE") >= 0) {
window.event.cancelBubble = true;
window.event.returnValue = false;
} else {
e.preventDefault();
}
}
function dragEnd() {
if (navigator.userAgent.indexOf("MSIE") >= 0) {
document.detachEvent("onmousemove", dragContinue);
document.detachEvent("onmouseup", dragEnd);
} else {
document.removeEventListener("mousemove", dragContinue, true);
document.removeEventListener("mouseup", dragEnd, true);
}
}