Draggable Div with Touch

CSS jQuery
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 with some more custom added code.
The changes made by me are marked with //VDWWD.

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.

The HTML source code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="shortcut icon" href="https://www.vanderwaal.eu/favicon.ico" />

    <title>VDWWD - Draggable Div with Touch</title>

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">

</head>
<body style="background-color: #4b4d66" class="pt-4">

    <!-- van der Waal Webdesign -->
    <!-- https://www.vanderwaal.eu -->

    <div class="container bg-white">

        <!-- demo content -->

        <div class="row">
            <div class="col-12 col-md-6 mx-auto pt-4">

                <div class="card draggable-div" id="DraggableTouchDiv1">

                    <div class="card-header d-flex justify-content-between draggable-div-header">
                        <h2 class="me-4">Draggable Touch div 1</h2>

                        <div class="float-end mt-1">
                            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-move">
                                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-move">
                                    <polyline points="5 9 2 12 5 15"></polyline>
                                    <polyline points="9 5 12 2 15 5"></polyline>
                                    <polyline points="15 19 12 22 9 19"></polyline>
                                    <polyline points="19 9 22 12 19 15"></polyline>
                                    <line x1="2" y1="12" x2="22" y2="12"></line>
                                    <line x1="12" y1="2" x2="12" y2="22"></line>
                                </svg>
                                <polyline points="5 9 2 12 5 15"></polyline>
                                <polyline points="9 5 12 2 15 5"></polyline>
                                <polyline points="15 19 12 22 9 19"></polyline>
                                <polyline points="19 9 22 12 19 15"></polyline>
                                <line x1="2" y1="12" x2="22" y2="12"></line>
                                <line x1="12" y1="2" x2="12" y2="22"></line>
                            </svg>
                        </div>
                    </div>
                    <div class="card-body">
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
                        <span class="d-none d-sm-block">
                            Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
                        </span>
                    </div>

                </div>

                <div class="card draggable-div" id="DraggableTouchDiv2">

                    <div class="card-body">
                        <strong>Draggable Touch div 2</strong>
                        <br />
                        <br />
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
                        <span class="d-none d-sm-block">
                            Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
                        </span>
                    </div>

                </div>

            </div>
        </div>

        <!-- footer -->

        <div class="row footer-row">
            <div class="col text-center pt-5 pb-3">

                <a target="_blank" href="https://www.vanderwaal.eu">
                    <img src="https://www.vanderwaal.eu/images/vdwwd.png" alt="van der Waal Webdesign" title="van der Waal Webdesign" width="300" height="95" />
                </a>

            </div>
        </div>

    </div>

    <!-- scripts -->

    <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>

    <!-- vdwwd scripts and stylesheets -->

    <script src="draggable-div-with-touch.js"></script>
    <link href="draggable-div-with-touch.css" rel="stylesheet">

    <script>
        $(function () {
            $('.draggable-div').draggableTouch();
        });
    </script>

</body>
</html>

The jQuery source code

(function ($) {

    //VDWWD: declare some extra variables
    var draggable_div_margin = 10;
    var draggable_div_emr = 0;
    var draggable_div_emb = 0;
    var draggable_div_zindex = 500;

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

    //VDWWD: extra margin bottom when there is a horizontal scrollbar
    if (document.body.scrollWidth > window.innerWidth) {
        draggable_div_emb = 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
                draggable_div_zindex++;
                $element.css('z-index', draggable_div_zindex);

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

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

                    //$(this).css({
                    //    top: touch.pageY - offset.y,
                    //    left: 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) {

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

                //$element.css({
                //    top: e.pageY - offset.y,
                //    left: 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
                draggable_div_zindex++;
                $element.css('z-index', draggable_div_zindex);

                //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 setDragDivCss($element, top, left) {

        //check if the element does not exceed the viewport at the top of bottom
        if (top < draggable_div_margin) {
            top = draggable_div_margin;
        } else if (top + $element.height() + draggable_div_margin + draggable_div_emb > window.innerHeight) {
            top = window.innerHeight - draggable_div_margin - $element.height() - draggable_div_emb;
        }

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

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