I found this interesting example for a javascript Drag/Drop on https://codepen.io/islempenywis/pen/VXqJVY.
However, there is an issue with it that if you click on the top of the "TODO Item" rectangle, after drag (MouseDown + Drag + MouseUp) it is not possible to drop this item; it sticks to the cursor and travels with it :/
MouseUp is a simple function, nothing fancy :
function onMouseUp(e, item) {
isMouseDown = false;
item.style.backgroundColor = "#F44336";
}
Since I can replicate it with Chrome, Edge and Firefox, I am guessing that this is a code problem but can't find out what that would be.
Please help.
Geo
There is a miscalculation in the onMouseMove event handler:
item.style.top = e.clientY + mouseOffset.y + "px";
When you move the mouse while holding the "TODO" item, the top of the whole element, including its margin, is being placed on the vertical coordinate of the viewport (e.clientY) where you clicked and adjusted to the point in the element where you clicked (+ mouseOffset.y), so the element moves along with the cursor. But it is ignoring its margin. If you pay attention, when you click and move the item, it will move down slightly. Those are 10 pixels of margin. When you click on the top, the element will be placed slightly down the cursor, the cursor will lose it, and it gets bugged. To fix, you have to substract the margin in the calculation.
item.style.top = e.clientY - 10 + mouseOffset.y + "px";
Related
I have recently tried to drag option box content in select element. It seems that ca not be done - drag doesn't fire at all.
I consider redesign this element totally that it will act & look as Select. Other option with jQuery is described here:
https://stackoverflow.com/questions/ask
In my case it must be done with Vanilla.JS.
I can of course back-engineer the above code, however maybe someone knows other working solution?
Make the element you wish to drag absolute and place it above the rest of the content using z-index. Then get the x and y coordinates of the element. Move the element directly into the body, center the element on pointer. Add an event listener for mousemove using a function to center the element at the page x/y coords. Function to drop element when you release the mouse button .onmouseup, this would remove all event listeners relevant to the moving of the element.
NOTE: This is very basic, more code would have to be used to determine page constraints in case user drags element out of the page bounding.
let drag = document.getElementById('draggableSpan');
drag.onmousedown = function(event) {
// make element absolute and place it on top with z-index
drag.style.position = 'absolute';
drag.style.zIndex = 1000;
let shiftX = event.clientX - drag.getBoundingClientRect().left;
let shiftY = event.clientY - drag.getBoundingClientRect().top;
// move it out of any current parents directly into body
// to make it positioned relative to the body
document.body.append(drag);
// function that centers the element at (pageX, pageY) coordinates
function moveTo(pageX, pageY) {
drag.style.left = pageX - shiftX + 'px';
drag.style.top = pageY - shiftY + 'px';
}
// move the absolutely positioned element under the pointer
moveTo(event.pageX, event.pageY);
function onMouseMove(event) {
moveTo(event.pageX, event.pageY);
}
// move the element on mousemove with event listener
document.addEventListener('mousemove', onMouseMove);
// drop the element on the page by removing eventlisteners that
// are relavent to the moving of the element
drag.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
drag.onmouseup = null;
};
};
<div class='parent'>
<span id='draggableSpan'>
draggable
</span>
</div>
I'm creating a box element that is resizable on all edges and corners. It seems to be working fine, except for one small thing. When I resize from the top or the left edge, the right (left edge resize) or bottom (top edge resize) will increase by a few px on each mousedown event.
I suspect that it has something to do with the width changing before the left has a chance to change, but even with a condition on the width adjustment, I get the same result.
if(onLeftEdge) {
console.log('box right: ' + b.right);
box.style.left = event.clientX + 'px';
box.style.width = (initialClientX - event.clientX + b.width) + 'px';
}
if(onTopEdge) {
box.style.top = event.clientY + 'px';
box.style.height = (initialClientY - event.clientY + b.height) + 'px';
}
As you resize on the left, the console will show the right jumping by 1-3px.
Why doesn't the right side of the box stay put. Why does it re-adjust on mousedown?
https://codepen.io/elephantjay/pen/yrRPLM?editors=1111
This was a really interesting problem. You had several problems in your code, but the jump itself stems from the fact that you are using getBoundingClientRect to get the width and the height. On your element, you have borders and the height and width returned by the above function would have their size added. So, when you would move the left or top border for any number of pixels, it would always add 2px to the added width/height (the total width of the left+right borders or top+bottom borders respectively).
What I changed:
I removed the listeners added on the HTML element because they were redundant and throwing errors
I used getBoundingClientRect for the top and left properties. I extracted that to a separate function that takes the scroll into consideration, as explained in this beautiful article.
I used getComputedStyle for the height and width
I changed the resize to be relative to the element itself, not the initial X and Y coordinates
I added the computed margin-top and margin-left in the equation so styling the element will not affect the resizing
A working copy is shown here: https://codepen.io/anon/pen/axeMge?editors=1111
I'm putting together a custom UI control in pure JS which simulates a schedule of events. One event is represented by a draggable div.
Here are the most relevant JS functions:
function onDrag(event)
{
console.log("**** DRAG ****");
if (dragging)
{
console.log("dragging = " + dragging);
//event.preventDefault();
currentY = event.clientY - initialY;
// Check whether we need to scroll up or down
//handleScroll();
// Limit dragging to size of background container
if(currentY < 0)
{
currentY = 0;
}
else if(currentY > background_height - draggable.offsetHeight)
{
currentY = background_height - draggable.offsetHeight;
}
else
{
currentY = currentY;
}
yOffset = currentY;
moveIt(currentY);
}
}
function moveIt(yPos)
{
console.log("moveIt : yPos = " + yPos);
draggable.style.transform = "translate3d( 0px, " + yPos + "px, 0)";
}
JSFiddle here
Everything basically works, but not surprisingly, it doesn't work as cleanly as I had hoped.
When dragging to the bottom of the parent container, the container should auto scroll. In practice, the draggable div follows the mouse until I drag below the container, at which point, the draggable bounces back about 300px upwards and plants itself towards the middle of the container (often partly or completely out of the viewport).
What I want is for the draggable to just park itself at the edge of the parent container, even if I try to drag it 1000px away. If I mouse very slowly, the container scrolls properly and the draggable stops on the edge, but if I'm sloppy and drag past the edge (as I anticipate most users will do), I get the bounce.
Sample console log depicting the bounce:
**** DRAG ****
dragging = true
moveIt : yPos = 712
**** DRAG LEAVE ****
**** DRAG ****
dragging = true
moveIt : yPos = 713
**** DRAG ****
dragging = true
moveIt : yPos = 713
**** DRAG ****
dragging = true
moveIt : yPos = 415
I can switch from HTML5 drag and drop and use a MouseEvent driven style, simply by commenting out the drag and drop listeners and substituting mouse events to avoid this bouncing effect. The fact that things work the way I want with mouse events suggests to me that I'm running into something undocumented with HTML5 DnD. I'm happy to switch to this mouse event driven approach, but then I have to implement scrolling manually and when I try that, the more I drag, the further the draggable moves from the mouse pointer. You can edit the fiddle, commenting out the DnD listeners
I'm happy to have a solution either the DnD or mouse event approaches, but mainly, I want to know what's causing these odd behaviors. Is there something wrong with the way I've structured the page? Is the CSS messed up? Is there some undocumented (but perhaps expected) behavior causing these problems?
I am using jquery UI draggable and want to have a draggable div animate during a drag event. Imagine that when stationary, the square div in the example is attached to a magnet. Wen you click on it and start dragging, it will not drag until a certain distance threshold is reached (distance = distance between center of div and current mouse position). After that threshold is reached, the div will animate toward the mouse and should then proceed to a normal drag event.
My problem is that when the mouse is dragged past the distance threshold at a fast enough speed, the div will start flashing because it is switching between showing the animated div and the one that is being dragged. Another problem that occurs is that if you drag the mouse fast enough and then stop, the div animates to the last recorded mouse position, but by the time the calculation is made, the mouse might already be at a different position, so the mouse is at one spot and the div is at another. Any ideas on how to make the div animate towards the mouse and then continue the drag event in one smooth transition? (also, i want the animation do be long enough so that you can see the div moving. if duration is set to 1, it works fine but I want it to be more visually appealing and smooth) Here is a demo: http://jsfiddle.net/b84wn2nf/
Here is some of the code found in the demo:
$(".dragSquare").draggable({
drag: function (event, ui) {
if($(this).hasClass("stuck")){
var mx = event.pageX;
var my = event.pageY;
ui.position.left = $(this).position().left;
ui.position.top = $(this).position().top;
var d = Math.floor(Math.sqrt(Math.pow(mx - ($(this).offset().left + ($(this).width() / 2)), 2) + Math.pow(my - ($(this).offset().top + ($(this).height() / 2)), 2)));
console.log(d)
if (d > 200) {
var animateToX = event.pageX - $(this).width() / 2;
var animateToY = event.pageY - $(this).height() / 2;
$(this).stop(true, true).animate({
left: animateToX + 'px',
top: animateToY + 'px'
}, {
/*easing: 'easeOutBounce,'*/
duration: 500,
start: function () {},
complete: function () {
$(this).removeClass("stuck");
}
});
}
}
} });
Okay, So I know I posted this a long time ago, and since then, I started to use the Snap.js SVG library instead of jQuery, which makes it much easier to cancel a drag, but before the switch, I solved the problem the only way I could: by modifying the source code.
In jquery-ui.js, locate the jQuery UI Draggable section, and then scroll down to _mouseDrag method.
What you need to do in your own code is set a global variable to tell jQuery if you want the drag behavior to be overridden. I used 'cancelDrag' as the variable name. So when its set to false, dragging behaves normally.
In _mouseDrag, you will see the following code:
this.helper[0].style.left = this.position.left + "px";
this.helper[0].style.top = this.position.top + "px";
What you need to do is wrap it in a conditional statement that depends on your boolean variable, so it looks like this:
if(!cancelDrag){
this.helper[0].style.left = this.position.left + "px";
this.helper[0].style.top = this.position.top + "px";
}
Basically, if cancelDrag is set to true, jQuery drag handler will not change the position of your element. Its not ideal, and should probably not be used, but it works. Make sure that if you modify this file, you are not using the minified source.
I am working on a small jQuery plugin that autoscrolls through a set of overflowing elements within a container div based on the mouse position within that container div.
See the Demo Here
The idea is for this plugin to be an improvement of something I wrote a while ago. See the autoscrolling navigation in the lower left here The old problem with this was that it jumps around when you mouseenter from anywhere but the bottom(javascript perspective) of the container div.
Now everything is working fine with my plugin but when you mouseenter from the top it screws up from time to time(move your mouse in and out fast and it will happen for sure), I think this is because I am getting different values from my mouseenter event and my mousemove event which are both used to calculate how to scroll the inner elements. Here is the function, the rest of the source is pretty small and decently commented.
projList.mousemove(function(e){
//keep it from freaking out when we mouseenter from Top of div
if(enterMouseY > divHeight){
enterMouseY = divHeight;
}
mouseY = e.pageY-projList.offset().top;
//ok that didnt work... try to keep it from freaking out when we mouseenter from Top of div
if (mouseY > divHeight){
mouseY = divHeight;
}
//distnace from top of container div to where our mouse Entered
var distToTop = divHeight - enterMouseY;
//here is the calculation, I parameterize the mouseY pos as a value between 0-1
//0 being where we entered and 1 being the top or bottom of the div
//then multiply that by how much we have to scroll to get to the end of the list
//are we moving up or down
if(mouseY>enterMouseY){
//if up calculate based on Top
var dist =totalScrollDistance * ((mouseY-enterMouseY-projList.offset().top)/(distToTop));
}else if(mouseY<enterMouseY){
//if up calculate based on Bottom
var dist =totalScrollDistance * ((mouseY-enterMouseY-projList.offset().top)/(enterMouseY));
}else if(mouseY = enterMouseY){
var dist = 0;
}
//set the position of the list offsetting wherever we left it
pos = dist+lastPos;
//scroll to that position
projList.scrollTop(pos);
//are we trying to scroll past the scrollable amount
if(pos<0){
pos = 0;
}
if(pos>totalScrollDistance){
pos = totalScrollDistance;
}
$('#div1').text("mouseY: "+ mouseY +" enterMouseY: "+ enterMouseY +" distance:"+ dist.toFixed(1) + " pos:"+ pos.toFixed(1));
});
I solved this problem, there was an error in my calculations, but works how I described above.
You can see it in action here
http://web.archive.org/web/20130529212243/http://www.robincwillis.com/AutoScroll/