Reputation: 480
I have some problems making a function to set positions to some divs with jQuery animate. I made a quiz with jQuery and want the right "answered" draggable divs with an numeric id to automatically go to thier target position div with the same id. This is for the navigation (everytime only 12 questions are shown).
Html looks like this
<div id="container"> //floats left
<div class="answer" id="1"></div> //droppables
<div class="answer" id="2"></div>
<div class="answer" id="3"></div>
<div class="answer" id="4"></div>
<div class="answer" id="5"></div>
<div class="answer" id="6"></div>
<div class="answer" id="7"></div>
<div class="answer" id="8"></div>
<div class="answer" id="9"></div>
<div class="answer" id="10"></div>
<div class="answer" id="11"></div>
<div class="answer" id="12"></div>
</div>
<div id="container>
<div class="drag" id="3"></div> //draggables are in random order
<div class="drag" id="1"></div>
<div class="drag" id="2"></div>
<div class="drag" id="6"></div>
<div class="drag" id="5"></div>
<div class="drag" id="12"></div>
<div class="drag" id="4"></div>
<div class="drag" id="8"></div>
<div class="drag" id="7"></div>
<div class="drag" id="11"></div>
<div class="drag" id="9"></div>
<div class="drag" id="10"></div>
</div>
Each time someone drops the right draggable on the right droppable the id is stored in an array named rightIds
.
Lets say rightIds is array(0=>3, 1=>7 2=>9)
I have also an array named currentIds
which contains the ids that are now shown.
currentIds
is now array(0=>1 to 11=>12)
I made this function:
function resetAnimation(currentIds, rightIds) {
$.each(currentIds, function (index, value) {
$.each(rightIds, function(ind, val) {
console.log('currentIds value: ' + value);
console.log('rightIds val: ' + val);
if (val == value) {
var target = $('#' + value + '.answer');
console.log(target);
var targetTop = target.offset().top;
console.log('targetTop: ' + targetTop);
var targetLeft = target.offset().left;
console.log('targetLeft: ' + targetLeft);
var drag = $('#' + value + '.drag');
console.log(drag);
var dragTop = drag.offset().top;
console.log('dragTop: ' + dragTop);
var dragLeft = drag.offset().left;
console.log('dragLeft: ' + dragLeft);
drag.animate({
'top': -target.offset().top,
'left': -target.offset().left
}, 3000);
}
});
});
}
But when I run it the drag divs are not going to the target position but to some other position. When I inspect the animated element the offset is the same as the target offset only with a "-" before the value. I hope someone can tell me what I'm doing wrong and how to solve this problem.
thanx
Upvotes: 2
Views: 2365
Reputation: 34178
I would use the simulate plugin that jQuery uses for testing to "simulate" drag and drop;
https://github.com/jquery/jquery-ui
Some examples of use can actually be viewed in the tests:
https://github.com/jquery/jquery-ui/blob/master/tests/unit/draggable/draggable_core.js
https://github.com/jquery/jquery-ui/blob/master/tests/unit/draggable/draggable_events.js
Upvotes: 0
Reputation: 6414
In your code, if your drag element is positioned within the same offset parent, and both are absolutely positioned it should be as simple as replacing:
drag.animate({
'top': -target.offset().top,
'left': -target.offset().left
}, 3000);
With:
drag.animate({
'top': target.offset().top,
'left': target.offset().left
}, 3000);
If it is not absolutely positioned, then it is not quite the same as setting the offset, jQuery is clever enough to work out the correct relative positioning but animate does not use this same functionality. That failing you can use:
$drag.animate(
{ top: $target.position().top - $drag.position().top
, left: $target.position().left - $drag.position().left }, 3000);
The HTML has been updated so that it is valid as correctly suggested by nnnnnn (no duplicate ids):
<div class="container">
<div class="answer" id="answer-1"></div>
<div class="answer" id="answer-2"></div>
<div class="answer" id="answer-3"></div>
<div class="answer" id="answer-4"></div>
<div class="answer" id="answer-5"></div>
<div class="answer" id="answer-6"></div>
<div class="answer" id="answer-7"></div>
<div class="answer" id="answer-8"></div>
<div class="answer" id="answer-9"></div>
<div class="answer" id="answer-10"></div>
<div class="answer" id="answer-11"></div>
<div class="answer" id="answer-12"></div>
</div>
<div class="container">
<div class="drag" id="drag-3"></div>
<div class="drag" id="drag-1"></div>
<div class="drag" id="drag-2"></div>
<div class="drag" id="drag-6"></div>
<div class="drag" id="drag-5"></div>
<div class="drag" id="drag-12"></div>
<div class="drag" id="drag-4"></div>
<div class="drag" id="drag-8"></div>
<div class="drag" id="drag-7"></div>
<div class="drag" id="drag-11"></div>
<div class="drag" id="drag-9"></div>
<div class="drag" id="drag-10"></div>
</div>
<div>
<a id="get-answers">Get answers</a>
</div>
With this the JavaScript can be simplified to match up the drag-[id] to the answer-[id] and using the position jQuery method to move the drag elements to the correct places:
// Animates right answered draggables
// to the right answers
function resetAnimation() {
// Iterate over all of the drag elements
$(".drag").each(function() {
var $drag = $(this)
// Get the id of the drag element
// by dropping "drag-" off the front
, id = $drag.attr("id").replace("drag-", "")
// Find the matching answer
, $target = $('#answer-' + id);
// The animation code, position gives
// the coordinates of the element relative
// to the offset parent, subtract the original
// position from the target position to get the
// correct translation deltas
$drag.animate(
{ top: $target.position().top - $drag.position().top
, left: $target.position().left - $drag.position().left}, 3000);
});
}
$(function() {
$("#get-answers").click(resetAnimation);
});
If you did want to stick to using the arrays to power this (I would use an object literal and treat the numbers as string ids personally), the modification to the code would be as follows:
// Animates right answered draggables
// to the right answers
function resetAnimation(answered, correct) {
// Iterate over all of the drag elements
for(var id in answered) {
if(answered[id] !== correct[id]) {
var $drag = $("#drag-" + id)
// Find the matching answer
, $target = $('#answer-' + correct[id]);
// The animation code, position gives
// the coordinates of the element relative
// to the offset parent, subtract the original
// position from the target position to get the
// correct translation deltas
$drag.animate(
{ top: $target.position().top - $drag.position().top
, left: $target.position().left - $drag.position().left}, 3000);
}
}
}
$(function() {
$("#get-answers").click(function() {
resetAnimation({
"1" : "3",
"2" : "2",
"3" : "1"
}, {
"1" : "2",
"2" : "3",
"3" : "1"
});
});
});
The CSS I am using for these test case:
.answer {
float: left;
margin: 10px;
background: red;
width: 20px;
height: 20px;
}
.drag {
position: relative;
float: left;
margin: 10px;
background: green;
width: 20px;
height: 20px;
}
.container {
float: left;
width: 100%;
}
Upvotes: 1
Reputation: 1334
You can use css class not only to give style, but also to designate elements.
You can do something like
<div class="answer" id="answer-2" class="answer q2"></div>
<div class="answer" id="answer-3" class="answer q3"></div>
...
<div class="drag" id="question-2" class="q2"></div>
<div class="drag" id="question-6" class="q6"></div>
Now, you can match questions and answer using theyr class. For instance, for each question, get the class var myClass = $(this).prop('class');
and find the matching answer $("div.answer."+myClass);
Upvotes: 0