Draggable Div with Touch

HTML JavaScript Website jQuery Draggable Div with Touch

A jQuery plugin that makes a div draggable with mouse and/or touch device. The code is a modified version of jquery-draggable-touch created by Jonatan Heyman.

The modified code makes sure that the div being dragged stays within the Viewport of the browser. And two lines with e.preventDefault(); are disabled so you can select/focus input elements within the draggable div.
The element that is being dragged is placed on top by setting the Z-index bij some more custom added code.
The changes made by me are marked with //VDWWD:.

View source on GitHub

Code Snippets

(function ($) {

    //VDWWD: declare some extra variables
    var margin = 10;
    var extra_margin_right = 0;
    var extra_margin_bottom = 5;
    var z_index = 100;

    //VDWWD: extra margin right when there is a vertical scrollbar
    if (document.body.scrollHeight > window.innerHeight) {
        extra_margin_right = 20;
    }

    //VDWWD: extra margin bottom when there is a horizontal scrollbar
    if (document.body.scrollWidth > window.innerWidth) {
        extra_margin_bottom = 20;
    }

    $.fn.draggableTouch = function (action) {
        // check if the device has touch support, and if not, fallback to use mouse
        // draggableMouse which uses mouse events
        if (!("ontouchstart" in document.documentElement)) {
            return this.draggableMouse(action);
        }

        // check if we shall make it not draggable
        if (action == "disable") {
            this.unbind("touchstart.draggableTouch");
            this.unbind("touchmove.draggableTouch");
            this.unbind("touchend.draggableTouch");
            this.unbind("touchcancel.draggableTouch");

            this.trigger("dragdisabled");

            return this;
        }

        this.each(function () {
            var $element = $(this);
            var offset = null;
            var draggingTouchId = null;

            var end = function (e) {
                //VDWWD: turned off preventDefault otherwise input elements in the div won't work
                //e.preventDefault();

                var orig = e.originalEvent;
                for (var i = 0; i < orig.changedTouches.length; i++) {
                    var touch = orig.changedTouches[i];
                    // the only touchend/touchcancel event we care about is the touch
                    // that started the dragging
                    if (touch.identifier != draggingTouchId) {
                        continue;
                    }

                    $element.trigger("dragend", {
                        top: orig.changedTouches[0].pageY - offset.y,
                        left: orig.changedTouches[0].pageX - offset.x
                    });

                    draggingTouchId = null;
                }
            };

            $element.bind("touchstart.draggableTouch", function (e) {
                var orig = e.originalEvent;
                // if this element is already being dragged, we can exit early, otherwise
                // we need to store which touch started dragging the element
                if (draggingTouchId) {
                    return;
                }

                draggingTouchId = orig.changedTouches[0].identifier;
                var pos = $(this).position();

                //VDWWD: move div to top with z-index
                z_index++;
                $element.css('z-index', z_index);

                offset = {
                    x: orig.changedTouches[0].pageX - pos.left,
                    y: orig.changedTouches[0].pageY - pos.top
                };

                $element.trigger("dragstart", pos);
            });

            $element.bind("touchmove.draggableTouch", function (e) {
                e.preventDefault();
                var orig = e.originalEvent;

                for (var i = 0; i < orig.changedTouches.length; i++) {
                    var touch = orig.changedTouches[i];
                    // the only touchend/touchcancel event we care about is the touch
                    // that started the dragging
                    if (touch.identifier != draggingTouchId) {
                        continue;
                    }

                    //$(this).css({
                    //    top: touch.pageY - offset.y,
                    //    left: touch.pageX - offset.x
                    //});

                    //VDWWD: use the setCss function
                    setCss($element, touch.pageY - offset.y, touch.pageX - offset.x);
                }
            });

            $element.bind("touchend.draggableTouch touchcancel.draggableTouch", end);
        });

        return this;
    };


    /**
     * Draggable fallback for when touch is not available
     */
    $.fn.draggableMouse = function (action) {
        // check if we shall make it not draggable
        if (action == "disable") {
            this.unbind("mousedown.draggableTouch");
            this.unbind("mouseup.draggableTouch");
            $(document).unbind("mousemove.draggableTouch");

            this.trigger("dragdisabled");

            return this;
        }

        this.each(function () {
            var $element = $(this);
            var offset = null;

            var move = function (e) {
                //$element.css({
                //    top: e.pageY - offset.y,
                //    left: e.pageX - offset.x,
                //});

                //VDWWD: use the setCss function
                setCss($element, e.pageY - offset.y, e.pageX - offset.x);
            };

            var up = function (e) {
                $element.unbind("mouseup.draggableTouch", up);
                $(document).unbind("mousemove.draggableTouch", move);

                $element.trigger("dragend", {
                    top: e.pageY - offset.y,
                    left: e.pageX - offset.x
                });
            };

            $element.bind("mousedown.draggableTouch", function (e) {
                var pos = $element.position();

                offset = {
                    x: e.pageX - pos.left,
                    y: e.pageY - pos.top
                };

                $(document).bind("mousemove.draggableTouch", move);
                $element.bind("mouseup.draggableTouch", up);
                $element.trigger("dragstart", pos);

                //VDWWD: move div to top with z-index
                z_index++;
                $element.css('z-index', z_index);

                //VDWWD: turned off preventDefault otherwise input elements in the div won't work
                //e.preventDefault();
            });
        });

        return this;
    };


    //VDWWD: separate function to keep the element withing the viewport and apply the corrected css
    function setCss($element, top, left) {
        //check if the element does not exceed the viewport at the top of bottom
        if (top < margin) {
            top = margin;
        } else if (top + $element.height() + margin + extra_margin_bottom > window.innerHeight) {
            top = window.innerHeight - margin - $element.height() - extra_margin_bottom;
        }

        //check if the element does not exceed the viewport at the left of right
        if (left < margin) {
            left = margin;
        } else if (left + $element.width() + margin + extra_margin_right > window.innerWidth) {
            left = window.innerWidth - margin - $element.width() - extra_margin_right;
        }

        //apply the position
        $element.css({
            top: top,
            left: left,
        });
    }
})(jQuery);

The CSS

.DraggableDiv {
    /* to allow the draggable div's to scroll with the content make the position absolute */
    position: fixed;
    top: 30%;
    left: 20%;
    width: 250px;
    border: 1px solid #000000;
    background: #beccf3;
    box-shadow: 5px 5px 3px 0px rgba(97,97,97,0.5);
}

.DraggableDiv div {
    padding: 5px;
}

.DraggableDiv .Header {
    cursor: move;
    font-weight: bold;
    color: #ffffff;
    border-bottom: 1px solid #000000;
    background-color: #566895;
}

#DraggableDiv2 {
    cursor: move;
    top: 40%;
    left: 25%;
}

The HTML

<div class="DraggableDiv" id="DraggableDiv1">
    <div class="Header" id="DraggableDiv1_Header">
        Header
    </div>
    <div class="Content">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
    </div>
</div>

<div class="DraggableDiv" id="DraggableDiv2">
    <div class="Content">
        <strong>No Header</strong>
        <br />
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
    </div>
</div>

<script> 
    $('.DraggableDiv').draggableTouch();
</script>

Draggable Touch Div 1

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Draggable Touch Div 2
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.