Friday, June 14, 2013

jQuery UI: Droppable Fundamentals

The draggable and droppable jQuery UI interaction components are cool.  But it turns out that after a recent experiment, I discovered a few things I had gotten wrong with the widgets in the past.  For example, reverting the draggable back to its original position on a failed drop, highlighting the droppable target while dragging, and changing the state of the draggable while dragging.  These are just the visible behaviors that help the user out.  There are a few things I'd got wrong with the code in the past too, so I've put together an example to illustrate some fundamental principles when working with these interactions.  Check out the comments in the code.



$(function() {

    // Creates the draggables.  We define this in a function
    // because we need to call it again, after the draggable 
    //has been moved to another parent.
    function draggables() {

        // The selector allows draggables() to be called any
        // time because it will only select books that 
        // aren't already draggable.
        $( ".book:not(:ui-draggable)" ).draggable({

            // Reverting the draggable to its original 
            // position is an important usability behavior.
            revert: "invalid",

            // The start and stop events allow us to change 
            // the state of the element while being dragged.
            start: function( e, ui ) {
                $( this ).addClass( "ui-state-active" );
            },

            stop: function( e, ui ) {
                $( this ).removeClass( "ui-state-active" );
            }

        });

    }

    // Create draggables.  Since there aren't any draggables 
    // yet, this creates all of them.
    draggables();

    // Create the two droppable group divs.
    $( ".group" ).droppable({

        // Highlighting the target droppable helps guide 
        // the user.
        activeClass: "ui-state-highlight",

        drop: function( e, ui ) {

            // The element has been dropped, and so we 
            // re-locate it to this droppable using 
            // appendTo(). The style attribute needs to be 
            // reset because the draggable component sets 
            // some styles that we no longer want or need.
            // Finally, we have to destroy the draggable 
            // component.  Otherwise, we can't drag it again
            // because the parent has changed.  And before 
            // the function returns, we re-create it using 
            // draggables().            
            ui.draggable.appendTo( this )
                        .attr( "style", "" )
                        .draggable( "destroy" );

            draggables();

        },

    });

    // It's good to be explicit in which draggables can go to
    // which droppables.
    $( "#group1" ).droppable(
        "option",
        "accept",
        "#group2 .book"
    );

    $( "#group2" ).droppable(
        "option",
        "accept",
        "#group1 .book"
    );

});