Zeebats
Zeebats

Reputation: 480

How to animate multiple divs to specific target location?

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

Answers (3)

Mark Schultheiss
Mark Schultheiss

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

Stuart Wakefield
Stuart Wakefield

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);

Example usage

Version 1 - jQuery matching

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);
});

Version 2 - Array matching

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%;
}

Here's a fiddle

Upvotes: 1

TCHdvlp
TCHdvlp

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

Related Questions