I'm creating a management application with some element on the page. Those element can be dragged around. but in the page you have 2 seperate places where it could be dragged.
So is there a way you can set more then one class in the containment option of the jQuery draggeble?
Thanks
Per containment:
Multiple types supported:
Selector: The draggable element will be contained to the bounding box of the first element found by the selector. If no element is
found, no containment will be set.
Element: The draggable element will be contained to the bounding box of this element.
String: Possible values: "parent", "document", "window".
Array: An array defining a bounding box in the form [ x1, y1, x2, y2 ].
Via https://api.jqueryui.com/draggable/#option-containment - #Vaindil mentioned this
The following is a "creative" answer regarding 'multiple containment selectors' not being directly supported by jQuery UI sortable. This is an "idea" which may help; in practice it did for my app:
You could set containment using Selector or Element (see above) mode with a higher-level parent element; meaning not just the actual 'parent' but maybe something a few DOM elements higher. (If you're using sortable, you can connect the two.) Then use the draggable method over to determine if you're on a dropzone.
You can also instantiate droppable on each dropzone. Here's some code that can help you determine what element you're over --- whereas this is highlighting all dropzone targets with a light-yellow bg class ('highlight') and the specific dropzone hovered with a bright-yellow bg class ('current-target'). Maybe you can use something like this to show the user where they're allowed to drop.
function droppable_on_deactivate_out() {
$('.dropzone').removeClass('target');
this.$dragElm.removeClass('over-outermost-parent');
this.$sortableElm.sortable('option', {
connectWith: this.activateConnectWith,
axis: 'y',
tolerance: 'pointer'
});
$(this).off('mousemove.droppableNamespace mouseout.droppableNamespace'); // Cleanup listeners
self.showGrid.tbody.animate({scrollTop: this.scrollBackTo}, 250);
}
$('.draggable').draggable({
// Or you could instantiate `sortable` instead of this `draggable` fn
});
$('#outermost-parent').droppable({
accept: '.draggable',
activate: function (droppableActivateEvent, ui) {
this.$dragElm = $(ui.draggable.context);
this.activateConnectWith = this.$dragElm.sortable('option', 'connectWith');
},
deactivate: droppable_on_deactivate_out,
over: function () {
$(this).on('mousemove.droppableNamespace', function (mousemoveEvent) {
$(mousemoveEvent.target)
.addClass('current-target')
.on('mouseout.droppableNamespace', function () {
$(this)
.removeClass('current-target')
.off('mousemove.droppableNamespace mouseout.droppableNamespace'); // Cleanup listeners
});
});
$('.dropzone').addClass('target');
this.$dragElm
.addClass('over-outermost-parent'); // Indicate something on UI
.sortable('option', {
connectWith: '#outermost-parent',
axis: false,
tolerance: 'intersect'
});
},
out: droppable_on_deactivate_out
});
Thus related to your containment question, depending on where the mouse/drag is (what it is over) you can alter the UI or the draggable option axis (etc) on-the-fly. Try some creative solutions like this; I hope it helps!
set the ui containment options like following:
containment:'selector_1, selector_2,...'
Related
I have a DOM element that has YUI event listeners on elements inside of it. I'm trying to get the DOM element to go on a different part of the page, but once I do that, I seem to lose the events that were set in YUI. Does anyone know of a way around this?
I'm facing exactly the same issue. I have both YUI and jQuery in the same app because it is a previously existing app originally built with YUI and I'm trying to use jQuery for implementing a functionality which is easier to achieve with jQuery.
As I have experienced, you shouldn't lose YUI events just by moving the element:
jQuery("#element1").before(jQuery("#element2"));
On the other hand, it will be a problem if you try to clone and swap elements, even using the clone function with arguments for cloning events as well:
var copy_to = $(to).clone(true);
var copy_from = $(this).clone(true);
$(to).replaceWith(copy_from);
$(this).replaceWith(copy_to);
According to that, I would say it is not possible to CLONE elements preserving events bound using YUI, but it is possible to MOVE them.
EDIT:
This is how I'm moving and swapping two DOM elements which have YUI events bound:
function makeElementAsDragAndDrop(elem) {
$(elem).draggable({
//snap : '#droppable',
snapMode : 'outer',
revert : "invalid",
helper: function(event) { // This is a fix as helper: "original" doesn't work well
return $(this).clone().width($(this).width());
},
zIndex : 100,
handle: ".title"
});
$(elem).droppable({
//activeClass : "ui-state-hover",
//hoverClass : "ui-state-active",
drop : function(event, ui) {
var sourcePlaceholder = $( "<div/>",
{
id: "sourcePlaceholder"
}).insertBefore(ui.draggable);
var targetPlaceholder = $( "<div/>",
{
id: "targetPlaceholder"
}).insertBefore(this);
$(ui.draggable).insertBefore(targetPlaceholder);
$(this).insertBefore(sourcePlaceholder);
sourcePlaceholder.remove();
targetPlaceholder.remove();
},
tolerance : "pointer"
});
}
As you can see, the code above is part of the DIVs drag&drop implementation I'm writing using jQuery.
Hope it helps.
PS: If somebody knows how to clone elements preserving events bound using YUI, please let us know!
I have a problem with jQuery UI and get some information of a dropped item.
I have three areas on the screen:
<div id="area1"></div>
<div id="area2"></div>
<div id="area3"></div>
In these areas, I put elements which are draggable with jQuery UI.
Now if an element is dropped from one to another area, I will not only get the area number in which the element is dropped to, I also want the area number where the element was dropped before the new drop.
I created a full working example: http://jsbin.com/iyaya3/
There is a blue draggable element and if I drag it from area1 to area2, I want to have the alert message with "dragged from area1 - dropped to area2".
How can this be done?
Best Regards, Tim-.
Hey, I updated your jsbin - http://jsbin.com/iyaya3/3
It works like this:
Take initial parent element's id and save it on draggable using jQuery.data
When dropping it on droppable, update data
I've got two ideas:
1) You can put a class or ID on the elements in each div:
<div id="area1"><node class="from1"></node></div>
<div id="area2"><node class="from2"></node></div>
<div id="area2"><node class="from2"></node></div>
And then test for that when you have the item
2) Write a function to do the clone instead of relying on the drag and drop to do it for you (http://ui-dev.jquery.com/demos/draggable/#option-helper) and then test for the parent and store that (in a singleton outside the scope of the dragger) where you can get it later.
I think I would prefer the first (even if you don't have html access to add these classes, just add it with javascript)
In your draggable setup, add this:
start: function(event,ui){
ui.helper.data('from-id', $(this).parent().attr('id') );
}
This will attach, as data, the ID of the container from which the element is being dragged.
Then, in the droppable, you can have:
alert( 'I was dragged from ' + ui.draggable.data('from-id') );
Here's the updated jsBin.
Edit: The start function creates a closure, so $(this).parent().attr('id') continues to point to the original parent. One solution to this problem (if you wish to keep a start function) is to clear start when the drag stops:
stop: function(event,ui){
ui.helper.draggable({ start: null });
}
This will allow the data methods in the droppable handlers to update the from-id without being immediately reverted to the original value by the start function.
Here's a revised jsBin example.
$(element.draggable).parent().attr('id')) works for me
I removed the duplicated code and solved the problem by saving the previous element id by the data() function.
JS Bin
<script type="text/javascript">
$(document).ready(function() {
$('#area1').append('<div class="shoulddraggable" style="width:100px;height:100px;top:0px;left:0px;background-color:blue;" data-elementid="100"></div>');
$(".shoulddraggable").draggable({
scroll: false,
revert: "invalid",
scope: "items",
});
$(".shoulddraggable").data('previousId', 'area1');
$('.droppable').droppable({
scope: "items",
drop: function(event, ui) {
alert('Previous container id: ' + ui.draggable.data('previousId'));
alert("Element: "+ui.draggable.data("elementid")+" dragged into" + $(this).attr('id'));
ui.draggable.data('previousId', $(this).attr('id'));
}
});
});
</script>
</body>
</html>
I have two set of draggable and two sets of dropable elements.
What I want is the first set of draggable elements to be droppable only inside the first set of droppables.
The second set of draggables should be droppable only to inside the second set of droppables.
A code snippet:
// this can be dropped only inside .drop elements
$('.drag').draggable({ revert: true });
$('.drop').droppable({
drop: function() {
//
}
});
// this can be dropped only inside .drop2 elements
$('.drag2').draggable({ revert: true });
$('.drop2').droppable({
drop: function() {
//
}
});
So a user won't be able to drop element with .drag class to .drop2 container.
This is what you want.
Use Accept property of droppable widget and revert property of draggable widget to achieve this.
I uses the jQuery pluins event.drag and event.drop
see a demo (just a reference question is not about restricting movement to a container)
Is it possible to prevent dropping of a drag element on the top of
another element? but it can be dragged over the other.
While you can accomplish what you want with the event.drag/drop script it is also easily done using jquery ui - draggable/droppable.
For the event., you need to return false if it meets yours criteria (or vice versa):
$('#drag1').drop("init",function(){
if ( $( this ).is('#badDiv') )
return false;
})
Here is the jquery ui version, it has a built in accept option in the $().droppable function - it accepts selectors also. This example is reverse of above. The above will let you move anywhere but badDiv, whereas this example below will won't let you move anywhere but the defined droppable:
script:
$("#drag1, #drag2").draggable({
revert: 'invalid'
});
$("#drop").droppable({
accept: '#drag1',
drop: function(event, ui) {
// whatever happens on drop
}
});
html:
<div id="drop" style="width:300px;height:300px;background:blue"></div>
<div id="drag1" style="width:50px;height:50px;background:green"></div>
<div id="drag2" style="width:50px;height:50px;background:yellow"></div>
This creates a big droppable area, but it will only accept the draggable ID of drag1. To see this effect you also need the option in your draggable for revert. This case I set it to invalid so the draggable will slide back to its original position if the drop is not valid.
I'm not sure of your exact application, but maybe this will point you in the right direction of what you're trying to accomplish.
I believe that the sorttables uses a similar setup, but that has more with appending divs and adjusting tables.
You can assign a valid drop target selector in this way.
$('.drag').drag(function( event, dd ){
// move the dragged element
},{ drop:'#drop1' }); // assign drop selector
You can also return some drop event handlers "false"
$('.drop').bind('dropinit drop',function( event, dd ){
if ( $( dd.drag ).is('#drag1') )
return false;
});
I have a draggable with a custom helper. Sometimes the helper is a clone and sometimes it is the original element.
The problem is that when the helper is the original element and is not dropped on a valid droppable it gets removed. My solution looks like this so far:
in my on_dropped callback I set ui.helper.dropped_on_droppable to true;
In the stop callback of the draggable, I check for that variable and then ... what do I do?
$('.my_draggable').draggable({
stop : function(e, ui) {
if (!ui.helper.dropped_on_droppable) {
/* what do I do here? */
}
},
Is this even the right approach?
Ok, I found a solution! It's ugly and it breaks the 'rules of encapsulation,' but at least it does the job.
Remember this is just for special cases! jQuery can handle its own helper removal just fine. In my case I had a helper that was sometimes the original element and sometimes a clone, so it wasn't always appropriate to delete the helper after reverting.
element.draggable({
stop : function(e, ui) {
/* "dropped_on_droppable" is custom and set in my custom drop method
".moved_draggable" is custom and set in my custom drag method,
to differentiate between the two types of draggables
*/
if (!ui.helper.dropped_on_droppable & ui.helper.hasClass('moved_draggable')) {
/* this is the big hack that breaks encapsulation */
$.ui.ddmanager.current.cancelHelperRemoval = true;
}
},
Warning: this breaks encapsulation and may not be forwards compatible
I might be missing something here, but is it not simply a case of adding
revert: "invalid"
to the options of the draggable if the draggable is of an original element, not a clone?
I use a custom helper that aggregates multiple-selection draggables into a single div. This does not seem to jive with the revert functionality so I came up with this scheme. The elements are manually appended back to the original parent which I keep track of via .data().
.draggable({
helper: function() {
var div = $(document.createElement('div'))
.data('lastParent', $(this).parent());
return div;
},
start: function() {
//... add multiple selection items to the helper..
},
stop: function(event,ui) {
$( $(ui.helper).data('lastParent') ).append( $(ui.helper).children() );
}
}
This approach does lose out on the pretty animation, but it may be useful to you or someone else with this issue.