jQuery drag and drop with sortable - javascript

I am having an issue trying to get an array out of JS using the sortable on some drag and drop elements.
A working demo can be found at:
Example
What I need to achieve:
Drag item on image, get x,y (done)
after all items have been placed, use jQuery sortable? to get array of id's with x,y coordinates.
array(23456->{xpos:234,ypos:234},23456->{xpos:234,ypos:234},....etc
I am not sure how to bind the sortable to the items only dropped on the image, and then get those values into an array
Here is the code so far:
jQuery(function($){
$('.dragThis').bind('click', function(){
$(this).css("border","3px solid #fff");
$(this).draggable({
containment: $('body'),
drag: function(){
$('#dropHere').droppable({
accept: '.dragThis',
over : function(){
$(this).animate({'border-width' : '3px', 'border-color' : '#0f0'}, 500);
}
});
},
stop: function(){
var position = $(this).position();
var parentPos = $('#dropHere').offset();
var xPos = position.left - parentPos.left;
var yPos = position.top - parentPos.top;
var finalOffset = $(this).position();
var finalxPos = xPos;
var finalyPos = yPos;
$('#finalX').text('Final X: ' + finalxPos);
$('#finalY').text('Final Y: ' + finalyPos);
//$('.dragThis').sortable();
console.log (finalxPos,finalyPos,$(this).attr("id"));
},
revert: 'invalid'
//connectToSortable: '#dropHere'
});
});
});
Any help getting pointed in the right direction, is greatly appreciated.
Just to clarify, I will be writing the values via AJAX to a DB for recall later.
Thanks in advance

Simple solution:
Add a class for those elements on their own 'drop' event, then get all of those have classes with a selector.
After that, a handy $.fn.each() and $.fn.offset() will help.
You can then work on those coordinates with top, left instead of your mentioned xpos, ypos.
Here is an example:
var coordinates = {};
$(".dropped").each(function(item) {
coordinates[$(item).attr("id")] = $(item).offset();
});

Related

Saving The Location of Multiple Objects Using JQuery

So I have multiple objects of the same class type, which are draggable. I want to save each of their location after dragging, so that when the page is reloaded, they are at the saved location.
Here is the code:
$(function(){
var Pos = JSON.parse(localStorage.getItem("position"));
var currentPos = {
top : 0,
left : 0
};
var c = [];
$('.framewrap').each(function(){
id = $(this).attr("id");
alert(Pos[id].top);
$(this).css({
top : Pos[id].top,
left : Pos[id].left
});
$(this).draggable({
drag: function(event,ui){
currentPos = $(this).position();
c.push(currentPos);
localStorage.setItem("position", JSON.stringify(c))
}
});
});
});
The position gets saved, but the problem is that both the objects are assigned to the same position, rather than different. Looks like the position gets over-written each time.
Any one highlight what I might be doing wrong ?

Extend jsplumb.draggable drag behavior

I am sure that I am missing something here, but I would like to extend the drag behavior of a div with jsPlumb.draggable class attributes that is attached to an endpoint, while preserving the jsPlumb.draggable attributes.
I would like something like this (adapted from this SO):
$('#dragcodes').draggable(
{
drag: function(){
var offset = $(this).offset();
var xPos = offset.left;
var yPos = offset.top;
console.log('x: ' + xPos);
console.log('y: ' + yPos);
}
});
on an element that is created using:
jsPlumb.draggable($(".dragcodes"));
Here is an example of what I am trying to do. I want the top box to read the position out on drag like the bottom one is, but cannot figure out how to hack the drag: function in jsPlumb.draggable. The binding behavior here is getting close, but I want to target the div that is attached to the endpoint. If I override the drag: functionality, I lose the jsPlumb.draggable attributes. Thanks in advance for your help.
In Version 1.6.3 the following Code works:
jsPlumb.draggable("#dragcodes", {
drag: function (event, ui) { //gets called on every drag
console.log(ui.position); //ui.position.left and ui.position.top
}
});
jsPlumb.draggable helps to update the DOM element whenever it is dragged. Instead you can write that code in jQuery draggable as:
$('#dragcodes').draggable(
{
drag: function(){
jsPlumb.repaint($(this)); // (or) jsPlumb.repaintEverything(); to repaint the connections and endpoints
//followed by your code
var offset = $(this).offset();
var xPos = offset.left;
var yPos = offset.top;
console.log('x: ' + xPos);
console.log('y: ' + yPos);
}
});
Now there is no need for jsPlumb.draggable($(".dragcodes"));
The best approach is to configure DragOptions of jsPlumb.Defaults. You have to specify drag function:
jsPlumb.getInstance({
....,
DragOptions: {
drag: function() {
}
},
....
);
JsPlumb Version 2.1.4
jsPlumb.draggable("#yourElement", {
drag: function (event) {
console.log(event.pos[0]); // for left position
console.log(event.pos[1]); // for top position
}
);
Or
jsPlumb.draggable("#yourElement", {
drag: function (event) {
$(event.el).position().left; // for left position
$(event.el).position().top; // for top position
}
);

in gamequery I am trying to move a selected object with mousetracker by clicking on the object, and dragging it

I know I can use mousedown selection for it, but I am wanting the clicked on sprite to follow my mouse, there is a mousetracker function of sorts mentioned in the api; but unfortunately there are no examples of this other than stating that it allows mouse detection.
//add mousedown events for yarnballs.
$(".gQ_sprite").mousedown(function() {
clickedDivId = $(this).attr('id');
if(clickedDivId.charAt(8) == "-")
{
currentClickedDivId = clickedDivId
$(document).mousemove(function(e){
spriteXPosition = e.pageX
spriteYPosition = e.pageY
});
}
});
I have the location of the mouse selected, just not sure how to get the selected sprite to follow it.
any help would be greatly appreciated.
What Mati said is correct: $.gQ.mouseTracker allows you to access the mouse's state outside of an event handler. The example he gives is correct but it can't be used to move a gQ object (sprite, tile-map or group) around because you'r not allowed to use the .css() function for those. Doing so will break collision detection.
If what you want is to move a gQ object you should do this instead :
$('#' + currentClickedDivId).xy($.gQ.mouseTracker.x, $.gQ.mouseTracker.y);
But since this should be done in a periodical callback, the smoothness of the dragging will depend on the refresh rate.
If you want to use event handlers instead you could modify you code to look like this (without using the mouseTracker):
var clickedDiv;
var clickedDivOffset = {x:0, y:0};
$(".gQ_sprite").mousedown(function(e) {
clickedDiv = $(this);
clickedDivOffset = {
x: e.pageX - clickedDiv.x() - $().playground().offset().left,
y: e.pageY - clickedDiv.y() - $().playground().offset().top
};
});
$(".gQ_sprite").mouseup(function() {
clickedDiv = false;
});
$().playground().mousemove(function(e) {
if(clickedDiv){
clickedDiv.xy(
e.pageX - clickedDivOffset.x,
e.pageY - clickedDivOffset.y,
);
}
});
This will implement a drag-n-drop effect. If you want the clicked element to stick to the mouse you will have to slightly adapt the code but the basics will remain the same.
According to the documentation:
If the mouse tracker is enabled you can check the state of the mouse at anytime by looking into the object $.gQ.mouseTracker where x and y contain the position of the mouse and 1, 2 and 3 a boolean value where true means that the first, second or thrid button is pressed.
Observe the output of:
$("#playground").playground({ refreshRate: 60, mouseTracker: true });
$.playground().startGame();
$.playground().registerCallback(function(){
console.log( $.gQ.mouseTracker );
}, 1000);
To make those divs actually follow the cursor, you have to use .css()
$('#' + currentClickedDivId).css({
top: $.gQ.mouseTracker.y + 'px',
left: $.gQ.mouseTracker.x + 'px'
});

How do I drag multiple elements at once with JavaScript or jQuery?

I want to be able to drag a group of elements with jQuery, like if I selected and dragged multiple icons on the Windows desktop.
I found the demo of threedubmedia's jQuery.event.drag:
http://threedubmedia.com/code/event/drag/demo/multi
http://threedubmedia.com/code/event/drag#demos
I think this plugin is great. Is this good and popular library? Do you know websites or applications which use it?
Are there any other libraries or plugins to drag multiple objects?
Can jQuery UI drag multiple objects?
var selectedObjs;
var draggableOptions = {
start: function(event, ui) {
//get all selected...
selectedObjs = $('div.selected').filter('[id!='+$(this).attr('id')+']');
},
drag: function(event, ui) {
var currentLoc = $(this).position();
var orig = ui.originalPosition;
var offsetLeft = currentLoc.left-orig.left;
var offsetTop = currentLoc.top-orig.top;
moveSelected(offsetLeft, offsetTop);
}
};
$(document).ready(function() {
$('#dragOne, #dragTwo').draggable(draggableOptions);
});
function moveSelected(ol, ot){
console.log(selectedObjs.length);
selectedObjs.each(function(){
$this =$(this);
var pos = $this.position();
var l = $this.context.clientLeft;
var t = $this.context.clientTop;
$this.css('left', l+ol);
$this.css('top', t+ot);
})
}
I am the author of the of the threedubmedia plugins. I added this functionality for supporting multiple elements, because I could not find a satisfactory solution anywhere else.
If you need a solution that works with the jQuery UI, here is a plugin which adds some multi-drag functionality, though the demos don't seem to work correctly in Firefox for Mac.
http://www.myphpetc.com/2009/11/jquery-ui-multiple-draggable-plugin.html
This worked for me.
Fiddle here:
var selectedObjs;
var draggableOptions = {
start: function(event, ui) {
//get all selected...
if (ui.helper.hasClass('selected')) selectedObjs = $('div.selected');
else {
selectedObjs = $(ui.helper);
$('div.selected').removeClass('selected')
}
},
drag: function(event, ui) {
var currentLoc = $(this).position();
var prevLoc = $(this).data('prevLoc');
if (!prevLoc) {
prevLoc = ui.originalPosition;
}
var offsetLeft = currentLoc.left-prevLoc.left;
var offsetTop = currentLoc.top-prevLoc.top;
moveSelected(offsetLeft, offsetTop);
selectedObjs.each(function () {
$(this).removeData('prevLoc');
});
$(this).data('prevLoc', currentLoc);
}
};
$('.drag').draggable(draggableOptions).click(function() {$(this).toggleClass('selected')});
function moveSelected(ol, ot){
console.log("moving to: " + ol + ":" + ot);
selectedObjs.each(function(){
$this =$(this);
var p = $this.position();
var l = p.left;
var t = p.top;
console.log({id: $this.attr('id'), l: l, t: t});
$this.css('left', l+ol);
$this.css('top', t+ot);
})
}
Thanks to ChrisThompson and green for the almost-perfect solution.
I wanted to add (this coming up high in google), since none of the plugins in this thread worked and it is not nativity supported by jquery ui, a simple elegant solution.
Wrap the draggable elements in a container and use an event to drag them all at once, this allows for singles draggables and multidraggables (but not really selective draggables).
jQuery(document).click(function(e) {
if(e.shiftKey) {
jQuery('#parent-container').draggable();
}
});
Check this out:
https://github.com/someshwara/MultiDraggable
Usage:$(".className").multiDraggable({ group: $(".className")});
Drags the group of elements together. Group can also be an array specifying individual elements.
Like:$("#drag1").multiDraggable({ group: [$("#drag1"),$("#drag2") ]});
Put your items into some container and make this container draggable. You will need to set handle option to be a class of your item element. Also you will need to recalculate items position after drag. And obviously when you deselect items you have to take them from this container and put back to their origin.
This is what i used, Worked in my case.
function selectable(){
$('#selectable').selectable({
stop: function() {
$('.ui-selectee', this).each(function(){
if ($('.ui-selectee').parent().is( 'div' ) ) {
$('.ui-selectee li').unwrap('<div />');
}
});
$('.ui-selected').wrapAll('<div class=\"draggable\" />');
$('.draggable').draggable({ revert : true });
}
});
};
there is Draggable in the jquery UI
all you would have to do is:
$(selector).draggable(); // and you are done!
see example here: http://jsfiddle.net/maniator/zVZFq/
If you really want multidragging you can try using some click events to hold the blocks in place
$('.drag').draggable();
$('.drag').click(function(){
console.log(this, 'clicked')
var data = $(this).data('clicked');
var all = $('.all');
if(data == undefined || data == false){
$(this).data('clicked', true);
this.style.background = 'red';
$(this).draggable('disable');
if(all.children().length <= 0){
all.draggable().css({
top: '0px',
left: '0px',
width: $(window).width(),
height: $(window).height(),
'z-index': 1
});
}
var top = parseInt(all.css('top').replace('px','')) +
parseInt($(this).css('top').replace('px',''))
var left = parseInt(all.css('left').replace('px','')) +
parseInt($(this).css('left').replace('px',''))
$(this).css({
top: top,
left: left
})
$('.all').append($(this));
}
else {
$(this).data('clicked', false);
this.style.background = 'grey';
$(this).draggable('enable');
$('body').append($(this));
if(all.children() <= 0){
all.draggable('destroy');
}
/*
var top = parseInt(all.css('top').replace('px','')) -
parseInt($(this).css('top').replace('px',''))
var left = parseInt(all.css('left').replace('px','')) -
parseInt($(this).css('left').replace('px',''))
$(this).css({
top: top,
left: left
})*/
}
})
See example here: http://jsfiddle.net/maniator/zVZFq/5

JQuery - animate moving DOM element to new parent?

I have an image tag inside of a table cell, that I'd love to move to another table cell, and have that movement animated.
The code looks something like this...
<td id="cell1"><img src="arrow.png" alt="Arrow"/></td>
<td id="cell2"></td>
I'd like to move "arrow.png" to "cell2", and have some kind of transition effect, preferably with JQuery.
Any ideas?
Thanks!
This is actually quite difficult because you have to remove and add it to the DOM but keep its position. I think you're looking for something like this. Basically we don't animate either the arrow in #cell1 or #cell2. We just create a new one in the body-tag and animate that. That way we don't have to worry about the table cell positions because we can position relative to the document.
var $old = $('#cell1 img');
//First we copy the arrow to the new table cell and get the offset to the document
var $new = $old.clone().appendTo('#cell2');
var newOffset = $new.offset();
//Get the old position relative to document
var oldOffset = $old.offset();
//we also clone old to the document for the animation
var $temp = $old.clone().appendTo('body');
//hide new and old and move $temp to position
//also big z-index, make sure to edit this to something that works with the page
$temp
.css('position', 'absolute')
.css('left', oldOffset.left)
.css('top', oldOffset.top)
.css('zIndex', 1000);
$new.hide();
$old.hide();
//animate the $temp to the position of the new img
$temp.animate( {'top': newOffset.top, 'left':newOffset.left}, 'slow', function(){
//callback function, we remove $old and $temp and show $new
$new.show();
$old.remove();
$temp.remove();
});
I think this should point you in the right direction.
#Pim Jager's answer is pretty good, however if you have object references to the original element they would break since the the original element was replaced with a clone
I came up with what I think is a slightly cleaner solution in that it only has a single clone that show up for animation then goes away, leaving the original in the new location.
function moveAnimate(element, newParent){
//Allow passing in either a jQuery object or selector
element = $(element);
newParent= $(newParent);
var oldOffset = element.offset();
element.appendTo(newParent);
var newOffset = element.offset();
var temp = element.clone().appendTo('body');
temp.css({
'position': 'absolute',
'left': oldOffset.left,
'top': oldOffset.top,
'z-index': 1000
});
element.hide();
temp.animate({'top': newOffset.top, 'left': newOffset.left}, 'slow', function(){
element.show();
temp.remove();
});
}
To use: moveAnimate('#ElementToMove', '#newContainer')
You'll need to do this in two steps: (1) animation (2) rehoming.
The animation you can take care of with .animate(), as #Ballsacian points out. The rehoming can be accomplished with .html() - for the example above,
var arrowMarkup = $('#cell1').html(); //grab the arrow
$('#cell1').html(""); //delete it from the first cell
$('#cell2').html(arrowMarkup); //add it to the second cell
Of course, you'll have to complicate that code to integrate the animation. And this way of doing it won't cause the selection (I'm assuming you're selecting a table row?) to activate rows between the old selection and the new one, as the arrow passes by them. That'd be even more complex to achieve.
I have extended one of the other answers a little further so that now you can pass an object as a third parameter which serves as a vehicle during the animation. For example, if you want to move some <li> from one <ul> to another, your <ul> likely has a certain class that gives the <li> its styling. So, it would really be handy to animate your <li> inside a temporary vehicle <ul> that provides for the same styling as either the source or the target <ul> of the animation:
//APPENDS AN ELEMENT IN AN ANIMATED FASHION
function animateAppendTo(el, where, float){
var pos0 = el.offset();
el.appendTo(where);
var pos1 = el.offset();
el.clone().appendTo(float ? float : 'body');
float.css({
'position': 'absolute',
'left': pos0.left,
'top': pos0.top,
'zIndex': 1000
});
el.hide();
float.animate(
{'top': pos1.top,'left': pos1.left},
'slow',
function(){
el.show();
float.remove();
});
}
I was trying #Davy8's function which is quite good, but I found it quite jarring when the moved element snapped off the page at the start then back in at the end. The other page elements suddenly shifting interrupted an otherwise smooth animation, but this likely would depend on your page layout.
So this is a modified version of #Davy8's function, which should also smoothly shrink and grow space between parents.
function moveAnimate(element, newParent,
slideAnimationSpeed/*=800*/, spacerAnimationSpeed/*=600*/)
{
//Allow passing in either a jQuery object or selector
element = $(element);
newParent= $(newParent);
slideAnimationSpeed=slideAnimationSpeed||800;
spacerAnimationSpeed=spacerAnimationSpeed||600;
var oldOffset = element.offset();
var tempOutgoing=element.clone().insertAfter(element);
tempOutgoing.hide(); //Don't take up space yet so 'newOffset' can be calculated correctly
element.appendTo(newParent);
var newOffset = element.offset();
var tempMover = element.clone().appendTo('body');
tempMover.css({
'position': 'absolute',
'left': oldOffset.left,
'top': oldOffset.top,
'z-index': 1000,
'margin':0 //Necessary for animation alignment if the source element had margin
});
element.hide();
element.show(spacerAnimationSpeed).css('visibility', 'hidden'); //Smoothly grow space at the target
tempMover.animate({'top': newOffset.top, 'left': newOffset.left}, slideAnimationSpeed, function(){
element.css('visibility', 'visible');
tempMover.remove();
});
tempOutgoing.show().css('visibility', 'hidden');
tempOutgoing.hide(spacerAnimationSpeed, function(){ tempOutgoing.remove() }); //smoothly shrink space at the source
}
If the animation doesn't have to be the thing moving, this question which uses fadeIn and fadeOut gives a simple, clean answer with no cloning and still conveys the motion quite well:
Re-ordering div positions with jQuery?
For anyone still viewing this, I found the provided examples didn't fit exactly what I wanted and they didn't account for margins, so here's my version:
jQuery.fn.extend({
moveElement : function (newParent, speed, after) {
var origEl = $(this);
var moveToEl = $(newParent);
var oldOffset = origEl.offset();
var temp = origEl.clone().appendTo('body');
temp.css({
'position' : 'absolute',
'left' : parseInt(oldOffset.left) - parseInt(origEl.css('margin-left')),
'margin' : origEl.css('margin'),
'top' : oldOffset.top,
'z-index' : 1000,
'height' : moveToEl.innerHeight(),
'width' : moveToEl.innerWidth()
});
var blankEl = $('<div></div>').css({
height : moveToEl.innerHeight(),
margin : moveToEl.css('margin'),
position : 'relative',
width : moveToEl.innerWidth()
});
if (after) {
origEl.insertAfter(moveToEl);
blankEl.insertAfter(newParent);
}
else {
origEl.insertBefore(moveToEl);
blankEl.insertBefore(newParent);
}
origEl.hide();
var newOffset = blankEl.offset();
temp.animate({
'top' : blankEl.offset().top - parseInt(moveToEl.css('margin-top')),
'left' : newOffset.left - parseInt(moveToEl.css('margin-left'))
}, speed, function () {
blankEl.remove();
origEl.show();
temp.remove();
});
}
});
Move an element before another: $('.elementToFind').moveElement('.targetElement', 1000);
Move an element after another: $('.elementToFind').moveElement('.targetElement', 1000, 'after');
JQuery http://docs.jquery.com/Downloading_jQuery
JQuery Effects http://docs.jquery.com/Effects/animate#paramsoptions
Example
$("#go1").click(function(){
$("#block1").animate( { width:"90%" }, { queue:false, duration:3000 } )
.animate( { fontSize:"24px" }, 1500 )
.animate( { borderRightWidth:"15px" }, 1500);
});

Categories

Resources