3

I have the following code for a drag and drop application which works as expected on desktop, however when I want to use the application on mobile the drag events do not work as expected. I am aware that touch events are required however I am not sure how to go about setting them up and implementing the functions.

<style>
    .objects {
        display:inline-block;
        background-color: #FFF3CC;
        border: #DFBC6A 1px solid;
        width: 50px;
        height: 50px;
        margin: 10px;
        padding: 8px;
        font-size: 18px;
        text-align: center;
        box-shadow: 2px 2px 2px #999;
        cursor: move;
    }
    #drop_zone {
        background-color: #EEE;
        border: #999 1px solid;
        width: 280px;
        height: 200px;
        padding: 8px;
        font-size: 18px;
    }
    </style>
  <h2 id="app_status">App status...</h2>
  <h1>Drop Zone</h1>
  <div id="drop_zone" ondragenter="drag_enter(event)" ondrop="drag_drop(event)" ondragover="return false" ondragleave="drag_leave(event)" ></div>
  <div id="object1" class="objects" draggable="true" ondragstart="drag_start(event)" ondragend="drag_end(event)">object 1</div>
  <div id="object2" class="objects" draggable="true" ondragstart="drag_start(event)" ondragend="drag_end(event)">object 2</div>
  <div id="object3" class="objects" draggable="true" ondragstart="drag_start(event)" ondragend="drag_end(event)">object 3</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script>
    function _(id){
       return document.getElementById(id);
    }
    var droppedIn = false;
    function drag_start(event) {
        _('app_status').innerHTML = "Dragging the "+event.target.getAttribute('id');
        event.dataTransfer.dropEffect = "move";
        event.dataTransfer.setData("text", event.target.getAttribute('id') );
    }
    function drag_enter(event) {
        _('app_status').innerHTML = "You are dragging over the "+event.target.getAttribute('id');
    }
    function drag_leave(event) {
        _('app_status').innerHTML = "You left the "+event.target.getAttribute('id');
    }
    function drag_drop(event) {
        event.preventDefault(); /* Prevent undesirable default behavior while dropping */
        var elem_id = event.dataTransfer.getData("text");
        _('app_status').innerHTML = "Dropped "+elem_id+" into the "+event.target.getAttribute('id');
        droppedIn = true;
          // Create our XMLHttpRequest object
          var hr = new XMLHttpRequest();
          // Create some variables we need to send to our PHP file
          var url = "jqueryserver.php";
          var vars = "value= "+elem_id;

          hr.open("POST", url, true);

          // Set content type header information for sending url encoded variables in the request
          hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
          // Access the onreadystatechange event for the XMLHttpRequest object
          hr.onreadystatechange = function() {
            if(hr.readyState == 4 && hr.status == 200) {
              var return_data = hr.responseText;
            document.getElementById("app_status").innerHTML = return_data;
            }
          }
          // Send the data to PHP now... and wait for response to update the status div
          hr.send(vars); // Actually execute the request
          document.getElementById("app_status").innerHTML = event.target.getAttribute('id')+"processing...";
        }

    function drag_end(event) {
        if(droppedIn == false){
            _('app_status').innerHTML = "You let the "+event.target.getAttribute('id')+" go.";
        }
        droppedIn = false;
    }

</script>
Aksen P
  • 4,564
  • 3
  • 14
  • 27
chan
  • 41
  • 1
  • 7
  • [HammerJs](https://hammerjs.github.io/) might be a good place to start. It's a simple and light weight library to add drag and touch events support in you web app. – abd995 Jun 21 '19 at 12:29
  • I was hoping to avoid using libraries, and get a solution similar to that proposed in https://stackoverflow.com/questions/1517924/javascript-mapping-touch-events-to-mouse-events – chan Jun 21 '19 at 12:57

2 Answers2

3

This question is old but i put there my example of dragging elements both with touch and mouse without any library

 /******************************
        required js */

        function makeDraggable(elmnt) {
            let pos1 = 0,
                pos2 = 0,
                pos3 = 0,
                pos4 = 0;

            let dragHandle = elmnt.getElementsByClassName("drag-handle")[0];


            if (dragHandle !== undefined) {
                // if present, the header is where you move the DIV from:
                dragHandle.onmousedown = dragMouseDown;
                dragHandle.ontouchstart = dragMouseDown; //added touch event

            } else {
                // otherwise, move the DIV from anywhere inside the DIV:
                elmnt.onmousedown = dragMouseDown;
                elmnt.ontouchstart = dragMouseDown; //added touch event
            }

            function dragMouseDown(e) {
                e = e || window.event;
                e.preventDefault();
               

                //Get touch or click position
                //https://stackoverflow.com/a/41993300/5078983
                if (e.type == 'touchstart' || e.type == 'touchmove' || e.type == 'touchend' || e.type == 'touchcancel') {
                    let evt = (typeof e.originalEvent === 'undefined') ? e : e.originalEvent;
                    let touch = evt.touches[0] || evt.changedTouches[0];
                    x = touch.pageX;
                    y = touch.pageY;
                } else if (e.type == 'mousedown' || e.type == 'mouseup' || e.type == 'mousemove' || e.type == 'mouseover' || e.type == 'mouseout' || e.type == 'mouseenter' || e.type == 'mouseleave') {
                    x = e.clientX;
                    y = e.clientY;
                }

                console.log("drag start x: "+x+" y:"+y);

                // get the mouse cursor position at startup:
                pos3 = x;
                pos4 = y;
                document.onmouseup = closeDragElement;
                document.ontouchend = closeDragElement;
                // call a function whenever the cursor moves:
                document.onmousemove = elementDrag;
                document.ontouchmove = elementDrag;
            }

            function elementDrag(e) {
                e = e || window.event;
                e.preventDefault();

                //Get touch or click position
                //https://stackoverflow.com/a/41993300/5078983
                if (e.type == 'touchstart' || e.type == 'touchmove' || e.type == 'touchend' || e.type == 'touchcancel') {
                    let evt = (typeof e.originalEvent === 'undefined') ? e : e.originalEvent;
                    let touch = evt.touches[0] || evt.changedTouches[0];
                    x = touch.pageX;
                    y = touch.pageY;
                } else if (e.type == 'mousedown' || e.type == 'mouseup' || e.type == 'mousemove' || e.type == 'mouseover' || e.type == 'mouseout' || e.type == 'mouseenter' || e.type == 'mouseleave') {
                    x = e.clientX;
                    y = e.clientY;
                }

                // calculate the new cursor position:
                pos1 = pos3 - x;
                pos2 = pos4 - y;
                pos3 = x;
                pos4 = y;
                // set the element's new position:
                elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
                elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
            }

            function closeDragElement() {
                console.log("drag end x: "+pos3+" y:"+pos4);
                // stop moving when mouse button is released:
                document.onmouseup = null;
                document.ontouchcancel = null; //added touch event
                document.ontouchend = null; //added touch event
                document.onmousemove = null;
                document.ontouchmove = null; //added touch event
            }
        }




        /*******************************
        test js */

        makeDraggable(document.getElementById("test-draggable"));
        makeDraggable(document.getElementById("test-draggable2"));
        makeDraggable(document.getElementById("test-full-draggable"));
 /******************************
        required css */

 .draggable {
     position: absolute;
     z-index: 9;
 }

 .drag-handle {
     cursor: move;
     z-index: 10;
 }

 .full-draggable {
     cursor: move;
 }

 /*******************************
        test css */

 .draggable {
     background-color: #f1f1f1;
     border: 1px solid #d3d3d3;
     text-align: center;
 }

 .draggable .drag-handle {
     padding: 10px;
     background-color: #2196F3;
     color: #fff;
 }

 #test-draggable2 {
     left: 200px
 }

 #test-full-draggable {
     left: 500px
 }
 <div id="test-draggable" class="draggable">
        <div class="drag-handle">Drag using me</div>
        I'm the <br>content, <br>you can't<br> drag touching<br> there
    </div>

    <div id="test-draggable2" class="draggable">
        <div class="drag-handle">Drag using me</div>
        It even works with many draggables<br>
        I'm the <br>content, <br>you can't<br> drag touching<br> there
    </div>

    <div id="test-full-draggable" class="draggable full-draggable">


        i don't have <br> a handle so <br> i'm completely <br> draggable
    </div>
Marco somefox
  • 370
  • 2
  • 10
  • Thanks for your effort!! Your code works in my Android Phone, but it doesn't in my Surface Book 2. No idea why, I implemented it in codesandbox [link](https://codesandbox.io/s/drag-and-drop-with-vanilla-javascript-for-touch-and-mouse-based-screens-o5456k?file=/src/index.js:2355-2709) and will experiment to see if I discover what is going on. If I do I'll report it here. Anybody is welcome to fork the project and identify the problem and report it here as a full 2022 updated answer. Oh, I noticed that the variables x and y were not defined so I defined them inside each function before the if's – Julio Spinelli Mar 20 '22 at 19:13
  • The event touchstart is not being detected by the javascript engine, it is being intercepted by the OS and used to scroll the background. So the touch screen is working but the event is not popping up. In Android the event is reaching the javascript engine and changing the size of the window when the element is scrolled. Not in the Surface Book – Julio Spinelli Mar 20 '22 at 19:13
  • if I add margin: 0; height: 100%; overflow: hidden to the body in the css, I am able to stop scrolling, but now not even the mouse events are popping to javascript engine. I'll look at touch-action: none; in the body in css. I'm mostly curious about why doesn't it work. Seems to be a lot of undocumented witchcraft around. – Julio Spinelli Mar 20 '22 at 19:15
  • Adding touch-action: none; to the css, according to MS doc. Stops the trapping of events by the system and now the mouse down events work for the margin: 0; height: 100%; overflow: hidden case, but the touchdown now does nothing, before it was highlighting with no scroll and scrolling in your original code. Same happens when I enable scrolling again – Julio Spinelli Mar 20 '22 at 19:15
  • found the problem and as promised included the solution as an answer to the original question since it broadens the answer by Marco [link](https://stackoverflow.com/users/5078983/marco-somefox) – Julio Spinelli Mar 20 '22 at 19:16
0

I found the problem with Marco's answer and the solution, so as promised I will include the answer here and leave the working code in codesandbox

Basically, the Surface Book 2 (not sure how widespread this is) didn't like the way Marco set up the listeners to the touch events. Something in the javascript engine or at the interface between the engine and the OS.

I just replaced the definition of the event listeners in this way in three places in his code (again whole working code available in the sandbox link above):

 // dragHandle.ontouchstart = dragMouseDown; //added touch event
 dragHandle.addEventListener("touchstart", dragMouseDown, false);

then:

    // document.ontouchend = closeDragElement;
    elmnt.addEventListener("touchend", closeDragElement, false);
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
    // document.ontouchmove = elementDrag;
    document.addEventListener("touchmove", elementDrag, false);

And finally in function closeDragElement:

// document.ontouchend = null; //added touch event
document.removeEventListener("touchend", closeDragElement, false);
document.removeEventListener("touchmove", elementDrag, false);
// document.ontouchmove = null; //added touch event

Wow! this was all, if this isn't witchcraft, what is it?

Julio Spinelli
  • 587
  • 3
  • 16