Using jQuery UI, I am trying to create an interface with two scrollable containers, each containing many draggable elements. The user can drag an element from one container to the other.
The dropping feature is not an issue. When dropped, the element is detached and recreated in the right place under its new parent.
My problem is that the draggable element cannot be displayed outside its container when the container has position:relative; applied, so while dragging, the element will disappear when it is moved outside the container boundaries.
This default behaviour makes sense, as normally the user would want to drag an element inside its container. As a workaround I had assumed the solution would be to use the draggable property 'appendTo', which I thought would move the element outside its container, but unfortunately this hasn't seemed to have had any effect.
DOM: (each view is scrollable and each container has position:relative and is as large as it needs to be to hold all elements)
BODY
VIEW 1
CONTAINER
DRAGGABLE ELEMENTS
VIEW 2
CONTAINER
DRAGGABLE ELEMENTS
Javascript:
$('div.card').draggable({
appendTo: 'body',
scroll: false //stops scrolling container when moved outside boundaries
});
Please see JSFiddle for a simplified explanation of the problem. I didn't want to bloat the example with droppable code, so this just illustrates the dragging issue. http://jsfiddle.net/Em7Ak/
Many thanks in advance.
I'm not sure if this will fix your exact problem, but I came across this question looking for the same answer and this is what I found.
In the options for .draggable(), pass in helper:'clone' to make a clone of the item automatically so that it can be dragged out of the container. And use appendTo:'body' to put it at the end of the <body> tag. So in my case, my options look somewhat like this, adding in revert:'invalid' to cause it to spring back if it isn't dropped somewhere valid:
jQuery(".myselector").draggable({
helper: 'clone',
revert: 'invalid',
appendTo: 'body'
});
use the "clone" helper and hide the item while dragging it and show it again on stop.
$(".removalbe-recom").draggable({
appendTo: "body",
helper: "clone",
revert: "invalid",
cursor: "move",
containment: "document",
zIndex: 10000,
scroll:false,
start: function (event, ui) {
$(this).hide();
},
stop: function (event, ui) {
$(this).show();
}
});
I had similar problem some months ago.
My need was to be able to use the auto scrolling of one big container from others
Here is my question for more details, JqueryUI, drag elements into cells of a scrolling dropable div containing large table
I found a workaround. The idea is to append the element clone to the scrollable container during the helper construction callback, then append the helper to the body using a setTimeout function after 1ms. The helper position must be mapped on the mouse position to avoid offset problem.
Here is my solution (JSFiddle seems to be down now, try it later if no code is displaying in the windows) : http://jsfiddle.net/QvRjL/134/
$('[id^="drag-"]').each(function() {
$(this).draggable({
opacity: 0.7,
cursorAt: { top: 15, left: 50 },
appendTo: 'body',
containment: 'body',
scroll: true,
helper: function(){
//Hack to append the cartridge to the body (visible above others divs),
//but still belonging to the scrollable container
$('#container').append('<div id="clone" class="cartridge">' + $(this).html() + '</div>');
$("#clone").hide();
setTimeout(function(){
$('#clone').appendTo('body');
$("#clone").show();
},1);
return $("#clone");
}
});
});
Add position:absolute to card style:
div.card {
position:absolute;
width:100px; height:50px;
border:1px black solid;
background-color:orange;
text-align:center; vertical-align:middle;
}
Related
I have a very simple script that utilises the jquery library and allows me to drag and resize the element with the class "moveabledraggable".
$(".moveabledraggable").draggable({
stop: function(){
alert('dragging stopped');
}
}).resizable({
stop: function(){
alert('resizing stopped');
}
});
It works perfectly, except I want "moveabledraggable" to be fixed on the screen as a user scrolls. It needs to be positioned using "fixed". Dragging the element doesn't alter my hardcoded inline style
position: fixed;
but on resizing the element, jquery ui changes it to
position: relative;
How can I stop this? Don't know if it's possible, but dragging and positioning (which is harder) doesn't change the position from fixed, so maybe it can be done.
Jsfiddle here!
(Jsfiddle doesn't actually work (possibly due to how it uses sub-windows within the viewport), but it can be copy-pasted to a jquery-ui linked local file.)
Ahh don't know why I didn't think of this at first. Just re-append inline css like this:
$(".moveabledraggable").draggable({
stop: function(){
alert('dragging stopped');
}
}).resizable({
stop: function(){
alert('resizing stopped');
$(".moveabledraggable").css("position", "fixed");
}
});
I am having trouble setting the containment value of the jqueryUI draggable function when using the [x1,y1,x2,y2] format. Here is the js:
<script type="text/javascript">
//slideTest.js
$(document).ready(function(){
$('.slideMin').each(function(){
$(this).draggable({
containment: [$(this).parent().offset().left,$(this).parent().offset().top,$(this).next('div').offset().left,$(this).parent().offset().top]
});
});
</script>
And the html which the script is interacting with:
<div class="slideBar">
<div class="slideMin"></div>
<div class="slideMax"></div>
</div>
I am trying to set the drag containment (of the slideMin div) based on the offset().left of the next div, in this case (slideMax).
In other words, I want the draggable behavior to stop (for the slideMin div) when it reaches the left of the next (slideMax) div.
The x1 seems to be working as the drag is stopping at the position defined, however, it is overlapping and extending beyond the next div, which i do not want to happen. There must be something wrong with how I am setting the x2 value but I am not sure what it is.
Any advice?
Figured it out... I had two divs, both draggable. The first div was to stop when it reached the next div. The above code accomplished this until I was to drag either div: once either div was dragged, the draggable range for the other div was not updating, therefore, I received the error where the draggable range was overlapping the other div. To fix it, I had to re-initiate and set the draggable range for the other div within the draggable function for the div that was being moved.
The new code goes something like this:
$(this).draggable({
containment: [$(this).position().left, $(this).position().top, $(this).next().position().left - $(this).width()+4, $(this).position().top],
drag: function(e){
$(this).next().draggable({
containment: [parseFloat($(this).position().left) + parseFloat($(this).width())-4, $(this).next().position().top, parseFloat($(this).parent().position().left) + parseFloat($(this).parent().width())-3, $(this).next().position().top]
});
}
});
$(this).draggable({
containment: [parseFloat($(this).prev().position().left) + parseFloat($(this).prev().width())-4, $(this).position().top, $(this).position().left, $(this).position().top],
drag: function(e){
$(this).prev().draggable({
containment: [$(this).parent().offset().left-$(this).prev().width()+5, $(this).prev().position().top, $(this).position().left - $(this).prev().width()+4, $(this).prev().position().top]
});
}
});
PS, the numbers -3 -4 +5, etc. were to add spacing for the handles on my draggable divs (4px handles), if you don't have handles you don't need the extra -3 -4 +5 portions of the code.
On a web page, I want to display a small div with text in it that is cut off and displayed with ellipses and when there is a mouse-over it should expand to a larger size such that all the text is visible. I then want to be able to use jqueryui drag and drop to move the text box onto a grid. When it is being dragged and placed, I want it to be the small size again.
I have attempted to do this like so:
Mouseover function:
$(function() {
$( ".ui-widget-content" ).mouseover(function(){
$(this).css("width", "200px");
$(this).css("height", "200px");
}).mouseout(function(){
$(this).css("width", "100px");
$(this).css("height", "40px");
})
})
Draggable function:
$(function() {
$( ".ui-widget-content" ).draggable({ snap: "td.chart:not(:first-child)", snapMode: "inner", revert: "invalid", snapTolerance: 40, stack: ".ui-widget-content",
drag: function( event, ui ) {
$(this).css("width", "100px");
$(this).css("height", "40px");
}
});
});
Droppable function:
$(function() {
$( "td.chart:not(:first-child)" ).droppable({
accept: ".ui-widget-content",
drop: function( event, ui ) {
$("#" + ui.draggable.attr("id") ).css("background-color","#D7F970");
}
});
});
Div that is getting dragged and dropped:
Lorem Ipsum
I thought that by using the above, when someone went to drag the div, first the mouse over would be triggered, enlargening the div, then the dragstart would shrink it and it would be fine. However, I am placing it on a table (see selector "td.chart:not(:first-child)" in droppable function), and while it works for the first three rows of the table, the div no longer snaps and gets dropped on the fourth and fifth rows. This problem does not occur if I remove the mouse-over function. Anyone understand what is going on here?
It would maybe help to take a look at your html.
It's just a hunch but maybe it's only a matter of the size of your draggable being too big and being over more than one droppable item.
I would advise trying to change the tolerance of your droppable element to see if it helps :
$( ".selector" ).droppable({ tolerance: "pointer" });
jQuery Documentation states :
Tolerance specifies which mode to use for testing whether a draggable is 'over' a droppable. Possible values:
fit: draggable overlaps the droppable entirely
intersect: draggable overlaps the droppable at least 50%
pointer: mouse pointer overlaps the droppable
touch: draggable overlaps the droppable any amount
When I drag the draggable, the element containing the droppables keeps getting triggered (because it's also a droppable), even though it's behind the droppables and they're stacked right on each other. It's behaving like there's a gap between the droppables, and there isn't one.
I've made an example on jsFiddle and here's the screenshot of the offending behavior.
If I add padding to .parent (for example padding: 0.2em 0.2em 0em 0.2em the behavior is even worse.
First of all, I hope you found out a solution in these months, I'm answering this because I'm working with jQueryUI in this period and I thought it would be a good exercise to try to find an answer. Also, I hate unanswered questions ^_^
Unfortunately, it really looks like the browser reacts as if between those .child elements there's some space, enough to trigger the *over events for the .parent. The only idea I came up with is to try to detect if, when the dropover events triggers on the parent, the mouse position is actually inside a child element. If so, you should give the accepting_drops class to the child element instead of the parent.
Here's the code (I made a jsFiddle to show my solution in action):
HTML and CSS are unaltered, so I won't copy them again
Javascript
$('.dragme').draggable({
helper: 'clone'
});
$('.parent,.child').droppable({
greedy: true,
tolerance: 'pointer',
});
$(".parent").on("dropover", function(event, ui) {
var first = $(".child:first");
var last = $(".child:last");
if((event.originalEvent.pageX > first.offset().left) &&
(event.originalEvent.pageY > first.offset().top) &&
(event.originalEvent.pageX <= (last.offset().left + last.width())) &&
(event.originalEvent.pageY <= (last.offset().top + last.height()))) {
$(this).removeClass("accepting_drops");
}
else {
$(this).addClass("accepting_drops");
$(".child").removeClass("accepting_drops");
}
}).on("dropout", function() {
$(this).removeClass("accepting_drops");
});
$(".child").on("dropover", function() {
$(".parent").removeClass("accepting_drops");
$(".child").removeClass("accepting_drops");
$(this).addClass("accepting_drops");
}).on("dropout", function() {
$(this).removeClass("accepting_drops");
});
I removed the hoverClass: 'accepting_drops' line because we're overriding the default behaviour of the draggable component. For the parent div, if when a dropover event triggers I'm also inside a child element, I remove the accepting_drops class from it, otherwise I remove it from any child who could have it and add it to the parent instead. When a dropout event triggers on it, I remove the accepting_drops class.
For the child, the behaviour is almost standard, on a dropover event I remove the accepting_drops class from everything else and add it to the child, on a dropout event I remove it.
The behaviour is still a bit of a mistery, but this workaround should do the trick.
I have one sortable which contains tiles. In addition, I have a bunch of droppables. I want the following interaction:
The user can drag tiles out of the sortable onto some droppable. Ideally, this would move the tile out of the sortable and append it to the droppable. In addition, the droppable should not accept anything anymore, i.e. there may be at most one tile on one droppable. I tried it in this fiddle http://jsfiddle.net/yXeMw/2/ but can't get the "move" to work.
Once that works, the user should also be able to move the tile out of the droppable back to the sortable, which I tried out here: (removed link due to 2 links limit, was version 3 of that same fiddle.) but fails too. (I only tried with an alert as I think the "move from .. to .." part should be the same.) See update 1.
I've been trying this for a few days and just can't figure it out.
PS: I've read tons of similar questions here on SO, but none is really the same as my issue, i.e. moving the element from the sortable to the droppable.
Edit: I would welcome an alternative solution using Mootools aswell.
Update 1: The droppable -> sortable direction only didn't work because my tiles inside the sortable had the float: left attribute which effectively made the sortable itself be of size 0px thus impossible to hover. Fixed fiddle: http://jsfiddle.net/yXeMw/5/
Update 2: Although I found a workaround (see my answer), I would still like to have a solution which moves the element. I couldn't get any of appendTo or append to work.
So, I found out how to emulate it. I am not 100% satisfied with this solution because it doesn't really move the element, so I will accept any better solution.
Rather, I create a new element, remove the old one and hide the helper. Both clone and appendTo don't seem to work.
Here is the fiddle: http://jsfiddle.net/VyfkE/1/
Aswell as the code in case the fiddle will get lost.
html:
<div class="slot">Drop one here</div>
<div class="slot">Or one here</div>
<div class="sortable">
<div class="tile">item 1</div>
<div class="tile">item 2</div>
<div class="tile">item 3</div>
</div>
css:
.slot {
background-color: forestgreen;
width:100px;
height:100px;
border: 1px solid black;
}
.sortable {
display:table-row;
background: #44F;
}
.tile {
display:table-cell;
background: firebrick;
border: 1px solid black;
width: 50px;
height: 25px;
}
and finally the javascript:
$(".slot").droppable({
drop: function(ev, ui) {
// Only want one tile per droppable!
if ($(this).children().length === 0) {
// Create the new element which will be inside the droppable.
cl = $('<div>').addClass('tile').text(ui.draggable.text()).css({
background: 'cornflowerblue'
});
// Make it draggable back into the sortable.
cl.draggable({
connectToSortable: '.sortable',
helper: 'clone' // <-- This is important!
});
$(this).append(cl);
// And remove the element from the sortable.
ui.helper.hide();
ui.draggable.remove();
}
}
});
$(".sortable").sortable({
connectWith: '.slot',
revert: true,
helper: 'clone', // <-- This is important, again!
receive: function(ev, ui) {
// If we get some item from a droppable, remove it from there.
ui.item.remove();
}
});