Cell boundary highlighting during drag

A simple example of droppable cell boundary highlighting during drags.

by EDWIN MAURICIO QUISHPE MALDONADO

HTML

<div class="draggable">New item 1</div>
<div class="draggable">New item 2</div>
<div class="draggable">New item 3</div>
<div class="draggable">New item 4</div>
<div class="draggable">New item 5</div>
<p>Drag the items above into the table below.</p>
<table>
    <tr class="droppable">
        <td>Item 1</td>
    </tr>
    <tr class="droppable">
        <td>Item 2</td>
    </tr>
    <tr class="droppable">
        <td>Item 3</td>
    </tr>
    <tr class="droppable">
        <td>Item 4</td>
    </tr>
    <tr class="droppable">
        <td>Item 5</td>
    </tr>
</table>

CSS

p {
    line-height: 32px;
}

table {
    width: 100%;
}

.draggable {
    background-color: #d0ffff;
    border: 1px solid black;
    cursor: pointer;
    padding: 6px;
}

.droppable {
    background-color: #ffffd0;
    border: 1px solid black;
}

.droppable td {
    padding: 10px;
}

.droppable-above {
    border-top: solid 3px blue;
}

.droppable-below {
    border-bottom: solid 3px blue;
}

JavaScript

$(document).ready(function() {
    $(".draggable").draggable({
        helper: "clone",
        drag: function(event, ui) {
            var $droppable = $(this).data("current-droppable");
            if ($droppable) {
                updateHighlight(ui, $droppable);
            }
        }
    });

    initDroppable($(".droppable"));

    function initDroppable($elements)
    {
        $elements.droppable({
            over: function(event, ui) {
                var $this = $(this);
                updateHighlight(ui, $this);
                ui.draggable.data("current-droppable", $this);
            },
            out: function(event, ui) {
                cleanupHighlight(ui, $(this));
            },
            drop: function(event, ui) {
                var $this = $(this);
                cleanupHighlight(ui, $this);
                var $new = $this.clone().children("td:first")
                                .html(ui.draggable.html()).end();
                if (isInUpperHalf(ui, $this)) {
                    $new.insertBefore(this);
                } else {
                    $new.insertAfter(this);
                }
                initDroppable($new);
            }
        });
    }

    function isInUpperHalf(ui, $droppable)
    {
        var $draggable = ui.draggable || ui.helper;
        return (ui.offset.top + $draggable.outerHeight() / 2
                <= $droppable.offset().top + $droppable.outerHeight() / 2);
    }

    function updateHighlight(ui, $droppable)
    {
        if (isInUpperHalf(ui, $droppable)) {
            $droppable.removeClass("droppable-below")
                      .addClass("droppable-above");
        } else {
            $droppable.removeClass("droppable-above")
                      .addClass("droppable-below");
        }
    }
        
    function cleanupHighlight(ui, $droppable)
    {
        ui.draggable.removeData("current-droppable");
        $droppable.removeClass("droppable-above droppable-below");
    }
});