Update:
Here is the final fiddle. Thanks Twisty.
https://jsfiddle.net/natjkern/55n1Lg61/14/
Here is a mock up of my site
http://jsfiddle.net/natjkern/pjq9wqLy/
On the right I have a fixed list of elements called #userList. This element must be set to overflow: auto, as the number of user elements is variable. I then need to drag a copy into dropzone, which again can vary in size, so it's container must be also set to overflow auto. I need the container to auto scroll when dragged element is dragged to edge to allow user to place element anywhere in dropzone. So far my best idea is to append to body first to escape the original auto overflow container then append to #dropzone on droppable({over:}). But this isn't really working out. Any UI experts out there that can give me a hand?
Here is my code so far:
$(document).ready(function () {
$('.moveMe').draggable({
helper: "clone",
appendTo: 'body',
revert: 'invalid',
scroll: true,
});
$('#dropZone').droppable({
accept: '.moveMe',
//here is where my problem arrises
// I need #dropZoneCon to scroll to place
over: function (event, ui) {
ui.helper.draggable.detach().appendTo($(this));
$('#DropZone div').css('position','absolute');
ui.helper.draggable.draggable('option', 'containment', 'parent');
},
/* */
drop: function (event, ui) {
ui.helper.draggable.detach().appendTo($(this));
$('#DropZone div').css('position','absolute');
ui.helper.draggable.draggable('option', 'containment', 'parent');
},
});
});
Note: commenting out over: allow me to place element where I would like, but I really need the scrolling to work.
This is a little clunky, but it does what you're looking to do.
http://jsfiddle.net/Twisty/55n1Lg61/5/
JQuery
$(document).ready(function() {
var tEntered = false;
$('.moveMe').draggable({
helper: "clone",
appendTo: 'body',
revert: 'invalid',
drag: function(e, u) {
var curPos = u.offset;
var $target = $("#dropZoneCon");
var tVP = $target.offset();
var tW = Math.floor($target.width());
var tH = Math.floor($target.height());
var pad = 20;
var scrInc = 10;
var x0 = tVP.top;
var x1 = tVP.top + tH;
var y0 = tVP.left;
var y1 = tVP.left + tW;
$("#dragInfo").html("Over #dropZone: " + tEntered + ", top: " +
tVP.top + "/" + curPos.top + "/" + x1 + " left: " + tVP.left + "/" + curPos.left + "/" + y1);
if (tEntered) {
// Increase Scroll
if (curPos.left >= (y1 - pad) && curPos.left <= y1) {
$target.scrollLeft($target.scrollLeft() + scrInc);
}
if (curPos.top >= (x1 - pad) && curPos.top <= x1) {
$target.scrollTop($target.scrollTop() + scrInc);
}
// Decrease Scroll
if (curPos.left >= y0 && curPos.left <= (y0 + pad)) {
$target.scrollLeft($target.scrollLeft() - scrInc);
}
if (curPos.top >= x0 && curPos.top <= (x0 + pad)) {
$target.scrollTop($target.scrollTop() - scrInc);
}
}
}
});
$('#dropZone').droppable({
accept: '.moveMe',
over: function(e, u) {
tEntered = true;
},
out: function(e, u) {
tEntered = false;
},
drop: function(event, ui) {
ui.draggable.detach().appendTo($(this));
$('#DropZone div').css('position', 'absolute');
ui.draggable.draggable('option', 'containment', 'parent');
},
});
});
So we do a lot of the heavy lifting in the drag event since we can determine the position of the drag object from this event. In the drag event we define a number of variables:
curPos the current position of our draggable object in { top, left }
$target the dropZone container
tVP the Target View Port offset in { top, left }
tW & tH the Target's Width and Height
scrInc a value we will use to increase or decrease the scroll position
pad the amount of pixels to use as the edge detection of tVP
x0 & x1 are the tVP top edge and bottom edge
y0 & y1 are the tVP left edge and right edge
I defined tEntered outside of these functions, so I can access it from either draggable or droppable. Since droppable has over and out, we can use those to change this value.
Now, when tEntered is true, meaning we are dragging an object over the target, we look to see if the x and y might be within our padding. If they are we increase or decrease the scroll position.
I found this helpful: http://www.jqwidgets.com/community/topic/how-detect-area-dropped-inside-a-div-into-another-div/
This should allow you to do what you want unless I misunderstood. Comment if you have questions.
Related
I have few components on my page which are draggable. They have the class
draggable. When this is dragged and stops I need to get the left and top positions
function init() {
var p = $('#groups-parent');
$('.draggable').draggable({
obstacle: ".obstacle",
preventCollision: true,
containment: [
p.offset().left, p.offset().top, p.offset().left + p.width() - 225, p.offset().top + p.height() - 50
],
start: function(event, ui) {
$(this).removeClass('obstacle');
},
stop: function (event, ui) {
console.log(ui);
$('#groups-parent').css({ 'min-height': Math.max(500, ui.position.top + 50) });
$(this).addClass('obstacle');
getTopLeftPosition();
}
});
}
Here is my function that should get the left and top positions.
function getTopLeftPosition(e) {
var coordX = e.pageX - $(this).offset().left,
coordY = e.pageY - $(this).offset().top;
alert(coordX + ' , ' + coordY);
}
This does not work. All I get in my browser is this error message
Cannot read properties of undefined (reading 'left')
How can I get this left and top positions? The alert should indicate it.
You probably just need to bind this to the function, by changing
getTopLeftPosition();
to either
getTopLeftPosition.call(this, event);
or
getTopLeftPosition.bind(this)(event)
I'm using jQuery UI slider and drag and drop to create a way of specifying a rating out of 100 for each div.
The problem is when I drag my divs onto the slider, I do not know how to get the value for the position of each div on the slider. Here is a plnk + some code;
http://plnkr.co/edit/wSS2gZnSeJrvoBNDK6L3?p=preview
$(init);
function init() {
var range = 100;
var sliderDiv = $('#ratingBar');
sliderDiv.slider({
min: 0,
max: range,
slide: function(event, ui) {
$(".slider-value").html(ui.value);
}
});
var divs = '.itemContainer'
$(divs).draggable({
containment: '#content',
cursor: 'move',
snap: '#ratingBar',
revert: function(event, ui) {
$(this).data("uiDraggable").originalPosition = {
top: 0,
left: 0
};
return !event;
}
});
var position = sliderDiv.position(),
sliderWidth = sliderDiv.width(),
minX = position.left,
maxX = minX + sliderWidth,
tickSize = sliderWidth / range;
$('#ratingBar').droppable({
tolerance: 'touch',
drop: function(e, ui) {
var finalMidPosition = $(ui.draggable).position().left + Math.round($(divs).width() / 2);
if (finalMidPosition >= minX && finalMidPosition <= maxX) {
var val = Math.round((finalMidPosition - minX) / tickSize);
sliderDiv.slider("value", val);
$(".slider-value").html(ui.value);
}
}
});
$(".slider-value").html(sliderDiv.slider('value'));
}
Hope someone can offer some advice,
Cheers
(Also, if someone knows why I can drop the divs outside of the rating bar on either side, please let me know!)
Jquery UI has a function called stop and there is where you want to handle all the calculations as such:
stop: function(event, ui) {
// Object dragged
var e = ui.helper;
// Offset of that object
var eOffset = e.offset().left;
// Sliders offset
var sliderOffset = sliderDiv.offset().left;
// Subtract their offsets
var totalOffset = eOffset - sliderOffset;
// Width of box dragged
var boxWidth = ui.helper.width();
// Subtract their widths to account for overflow on end
var sliderW = sliderDiv.width() - boxWidth;
// Get percent moved
var percent = totalOffset / sliderW * 100;
// Find .slider-value and replace HTML
e.find(".slider-value").html(Math.round(percent));
}
This will be located here:
$(divs).draggable({
containment: '#content',
cursor: 'move',
snap: '#ratingBar',
revert: function(event, ui) {
$(this).data("uiDraggable").originalPosition = {
top: 0,
left: 0
};
return !event;
},
.... <-- HERE
});
I have provided commenting for every line so you understand what I did.
Above your draggable function you need to define a tighter containment area as such:
var left = sliderDiv.offset().left;
var right = left + sliderDiv.width();
var top = sliderDiv.offset().top;
var bottom = top + sliderDiv.height();
var height = $(".itemContainer").height();
var width = $(".itemContainer").width();
$(divs).draggable({
containment: [left, 0, right - width, bottom - height],
.....
});
This will prevent the draggable from being moved left, right, or below the slider.
Basically you grab the position of the object grabbed, adjust for some offset, adjust for some width, calculate the percentage, and replace the html (Could use text() as well).
Here is your plunker redone: http://plnkr.co/edit/3WSCo77c1cC5uFiYO8bV?p=preview
Documentation:
Jquery UI
Jquery .find
I am attempting to allow sorting items that have a long height. The height of the objects may be more then the users screen, which will make it difficult to click and drag the items (and still have a general overview of what the items are.
I devised a simple plan to go and change the zoom level of the items when beginning the start of the drag, but this does not seem to correctly work.
See the JSFiddle and try moving the blocks around to see the issue: http://jsfiddle.net/sh4zx63w/7/
$('#container ul').sortable({
revert: true,
placeholder: "dropstyle",
start: function (evt, ui) {
pointerY = (evt.pageY - $('#container').offset().top) / zoom - parseInt($(evt.target).css('top'));
pointerX = (evt.pageX - $('#container').offset().left) / zoom - parseInt($(evt.target).css('left'));
},
beforeStop: function (event, ui) {
$('#container').css({
zoom: 1,
'overflow': 'none',
'height': 'auto'
})
},
drag: function (evt, ui) {
var canvasTop = $('').offset().top;
var canvasLeft = $('#container').offset().left;
var canvasHeight = $('#container').height();
var canvasWidth = $('#container').width();
// Fix for zoom
ui.position.top = Math.round((evt.pageY - canvasTop) / zoom - pointerY);
ui.position.left = Math.round((evt.pageX - canvasLeft) / zoom - pointerX);
// Check if element is outside canvas
if (ui.position.left < 0) ui.position.left = 0;
if (ui.position.left + $(this).width() > canvasWidth) ui.position.left = canvasWidth - $(this).width();
if (ui.position.top < 0) ui.position.top = 0;
if (ui.position.top + $(this).height() > canvasHeight) ui.position.top = canvasHeight - $(this).height();
// Finally, make sure offset aligns with position
ui.offset.top = Math.round(ui.position.top + canvasTop);
ui.offset.left = Math.round(ui.position.left + canvasLeft);
}
});
I have tried the click and dragging on Firefox, Chrome and Internet Explorer, and all of them are not working correctly.
In Chrome and Internet Explorer, dragging the brown box (the first box) is not possible, you need to move the smaller boxes up if you want to push the box down.
Firefox is better, but it still has problems, which can be seen when attempting to drag the brown box to the bottom. Dragging the smaller boxes appears to work much better.
I have a page full of draggable divs only in horizontal (on axis X).
When I'm in a touch device I can't scroll down the page, due conflicts between scroll and drag.
Here's a jsfiddle example (test in touch devices and try to scroll).
My draggable code:
$(".cell").draggable({
axis: "x",
drag: function (event, ui) {
var size = $(".cell").width();
if (ui.offset.left > size - em(4)) {
ui.position.left = size - em(4);
}
},
start: function (event, ui) {
if (initialPosition == null) {
initialPosition = ui.position.left;
}
$(".cell").not(ui.helper).each(function () {
var pos = $(this).position().left;
if (pos > 0) {
$(this).animate({
left: initialPosition
}, 200, null);
}
});
},
stop: function (event, ui) {
var size = $(".cell").width();
if (ui.position.left > initialPosition) {
if (ui.position.left - initialPosition >= size / 3) {
ui.helper.animate({
left: size - em(4)
}, 200, null);
} else {
ui.helper.animate({
left: initialPosition
}, 200, null);
}
}
}
});
I want to detect if the user is scrolling vertically before start dragging and cancel the
horizontal dragging.
Help me, please. How can I make this work?
I had a problem similar to this one and eventually found a fairly simple solution.
In my scenario I had an inbox list whose items that you could drag to the left or right to expose action buttons. The entire inbox item must be draggable -- so the use of a drag handle was not an option.
jQuery's draggable prevents vertical scrolling on touch screens if the touch was initiated inside a draggable element. So if the screen was filled with draggable inbox items, then the user would become trapped -- unable to scroll up or down.
The solution that worked for me was to measure any change in the cursor's vertical position and use window.scrollBy to manually scroll the window by the same amount:
var firstY = null;
var lastY = null;
var currentY = null;
var vertScroll = false;
var initAdjustment = 0;
// record the initial position of the cursor on start of the touch
jqDraggableItem.on("touchstart", function(event) {
lastY = currentY = firstY = event.originalEvent.touches[0].pageY;
});
// fires whenever the cursor moves
jqDraggableItem.on("touchmove", function(event) {
currentY = event.originalEvent.touches[0].pageY;
var adjustment = lastY-currentY;
// Mimic native vertical scrolling where scrolling only starts after the
// cursor has moved up or down from its original position by ~30 pixels.
if (vertScroll == false && Math.abs(currentY-firstY) > 30) {
vertScroll = true;
initAdjustment = currentY-firstY;
}
// only apply the adjustment if the user has met the threshold for vertical scrolling
if (vertScroll == true) {
window.scrollBy(0,adjustment + initAdjustment);
lastY = currentY + adjustment;
}
});
// when the user lifts their finger, they will again need to meet the
// threshold before vertical scrolling starts.
jqDraggableItem.on("touchend", function(event) {
vertScroll = false;
});
This will closely mimic native scrolling on a touch device.
I use jquery.ui.touch-punch.js and this worked for me, line 38:
event.preventDefault();
Reference
I'm trying to make a jquery function to follow the mouse coursor with a div, when it is on mousedown and when it is on mouseup it stay in the last position it was.
any sugestion.
Why not simply use drag and drop by jquery:
<script>
$(function() {
$( "#draggable" ).draggable();
});
</script>
Jquery draggable
I've put together a simple working example that defines a Draggable object. You specify the drag item (the element that you're moving around), as well as a drag boundary (the space—or element—that you are moving the item inside of). The concept of a boundary is important if you ever want to restrict a draggable item to a certain space on the page (such as a container), or define a relative coordinate system on which to base your math.
My solution isn't the fastest, but it demonstrates the concept:
$(function() {
window.mousedown = 0;
$(window).on('mousedown mouseup', function(e) {
if(e.type == 'mousedown') { this.mousedown++; }
else { this.mousedown--; }
});
var Draggable = function(dragItem, dragBoundary) {
this.item = $(dragItem).css('position', 'absolute');
this.item.on('mousemove', $.proxy(this.handleDragEvent, this));
this.boundary = $(dragBoundary).css('position', 'relative');
};
Draggable.prototype.handleDragEvent = function(e) {
if(window.mousedown) {
var mousePosition = this.mapToBoundary([e.clientX, e.clientY]);
var mouseX = mousePosition[0],
mouseY = mousePosition[1];
if(typeof this.prevMouseX == "undefined") this.prevMouseX = mouseX;
if(typeof this.prevMouseY == "undefined") this.prevMouseY = mouseY;
this.itemX = this.item.offset().left - this.boundary.offset().left;
this.itemY = this.item.offset().top - this.boundary.offset().top;
var deltaX = mouseX - this.prevMouseX,
deltaY = mouseY - this.prevMouseY;
this.item.css({
'left': this.itemX + deltaX,
'top': this.itemY + deltaY
});
this.prevMouseX = mouseX;
this.prevMouseY = mouseY;
}
};
Draggable.prototype.mapToBoundary = function(coord) {
var x = coord[0] - this.boundary.offset().left;
var y = coord[1] - this.boundary.offset().top;
return [x,y];
};
var draggable = new Draggable($('.draggable'), $('.container'));
});
Notice that we are maintaining a mousedown value on global, allowing us to determine when it would be appropriate to drag around our element (we only add a mousemove listener to the drag item itself). I've also included a spacer div above the boundary div to demonstrate how you can move the boundary anywhere around the page and the coordinate system is still accurate. The code to actually restrict a draggable item within its assigned boundary could be written using simple math.
Here is the fiddle: http://jsfiddle.net/bTh9s/3/
EDIT:
Here is the start to some code for restricting a Draggable item within its container.
Draggable.prototype.restrictItemToBoundary = function() {
var position = this.item.position();
position.right = position.left + this.item.outerWidth();
position.bottom = position.top + this.item.outerHeight();
if(position.left <= 0) {
this.item.css('left', 1);
} else if(position.right >= this.boundary.outerWidth()) {
this.item.css('left', this.boundary.outerWidth() - this.item.outerWidth());
}
if(position.top <= 0) {
this.item.css('top', 1);
} else if(position.bottom >= this.boundary.outerHeight()) {
this.item.css('top', this.boundary.outerHeight() - this.item.outerHeight());
}
};
This method should be called inside of Draggable.handleDragEvent just after you update the CSS positioning of the drag item. It seems this solution is glitchy, but it's a start.