最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Imitating Drag and Drop Events with Touch Events for Mobile Devices - Stack Overflow

programmeradmin6浏览0评论

Some time ago I was struggling with drag and drop in web browsers on mobile devices. The default javascript events do not work on mobile. You can use only touch events.

In my case, I need to swap two images and also the IDs by doing drag and drop. Here an example:

div{
  display:inline-block;
  border:1px solid #0b79d0;
}

div, img{
        width:120px;
        height:120px;
    }
<div id="1" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
    <img id="a" draggable="true" ondragstart="dragStart(event)" src=".jpg"/>
</div>

<div id="2" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
    <img id="b" draggable="true" ondragstart="dragStart(event)" src=""/>
</div>

<div id="3" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
    <img id="c" draggable="true" ondragstart="dragStart(event)" src="/wp-content/uploads/2018/10/animals-for-dolphin-drawings-pencil-drawings-pinterest-verwandt-mit-delfine-zeichnen-100x100.jpg"/>
</div>

<script>
    function allowDrop(ev){
        ev.preventDefault();
    }

    function dragEnter(ev){
        var element = document.getElementById(ev.target.id);
        element.style.border = "dotted";
        element.style.borderColor = "#0b79d0";
    }

    function dragLeave(ev){
        var element = document.getElementById(ev.target.id);
        element.style.border = "1px solid #0b79d0";
    }

    function dragStart(ev){
        ev.dataTransfer.setData("src", ev.target.id);
        var number = ev.target.id.replace ( /[^\d.]/g, '' );
        ev.dataTransfer.setData("text/plain", number);
    }

    function drop(ev) {
        ev.preventDefault();
        var src = document.getElementById(ev.dataTransfer.getData("src"));

        var srcParent = src.parentNode;
        var tgt = ev.currentTarget.firstElementChild;
        ev.currentTarget.replaceChild(src, tgt);
        srcParent.appendChild(tgt);

        var number1 = srcParent.id.replace(/[^\d.]/g, '');
        var number2 = ev.currentTarget.id.replace(/[^\d.]/g, '');

        var element = document.getElementById(ev.target.id);
        element.style.border = "solid 1px #0b79d0";

        var number = ev.target.id.replace(/[^\d.]/g, '');
    }
</script>

Some time ago I was struggling with drag and drop in web browsers on mobile devices. The default javascript events do not work on mobile. You can use only touch events.

In my case, I need to swap two images and also the IDs by doing drag and drop. Here an example:

div{
  display:inline-block;
  border:1px solid #0b79d0;
}

div, img{
        width:120px;
        height:120px;
    }
<div id="1" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
    <img id="a" draggable="true" ondragstart="dragStart(event)" src="https://static.webshopapp./shops/073933/files/156288269/345x345x1/artibalta-white-tiger.jpg"/>
</div>

<div id="2" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
    <img id="b" draggable="true" ondragstart="dragStart(event)" src="https://yt3.ggpht./a-/AN66SAyfsmao4f1EEOqkBP2PgpSUcabPJXLZ1sLEnA=s288-mo-c-c0xffffffff-rj-k-no"/>
</div>

<div id="3" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
    <img id="c" draggable="true" ondragstart="dragStart(event)" src="https://kinderbilder.download/wp-content/uploads/2018/10/animals-for-dolphin-drawings-pencil-drawings-pinterest-verwandt-mit-delfine-zeichnen-100x100.jpg"/>
</div>

<script>
    function allowDrop(ev){
        ev.preventDefault();
    }

    function dragEnter(ev){
        var element = document.getElementById(ev.target.id);
        element.style.border = "dotted";
        element.style.borderColor = "#0b79d0";
    }

    function dragLeave(ev){
        var element = document.getElementById(ev.target.id);
        element.style.border = "1px solid #0b79d0";
    }

    function dragStart(ev){
        ev.dataTransfer.setData("src", ev.target.id);
        var number = ev.target.id.replace ( /[^\d.]/g, '' );
        ev.dataTransfer.setData("text/plain", number);
    }

    function drop(ev) {
        ev.preventDefault();
        var src = document.getElementById(ev.dataTransfer.getData("src"));

        var srcParent = src.parentNode;
        var tgt = ev.currentTarget.firstElementChild;
        ev.currentTarget.replaceChild(src, tgt);
        srcParent.appendChild(tgt);

        var number1 = srcParent.id.replace(/[^\d.]/g, '');
        var number2 = ev.currentTarget.id.replace(/[^\d.]/g, '');

        var element = document.getElementById(ev.target.id);
        element.style.border = "solid 1px #0b79d0";

        var number = ev.target.id.replace(/[^\d.]/g, '');
    }
</script>

So the dragStart event stores information, such as the image. However, such operation is not possible with touch events.

Now I was wondering, is there a way to do the same thing on mobile, by just imitating the drag events by using the touch events?

Share Improve this question asked Nov 29, 2018 at 1:20 PLAYCUBEPLAYCUBE 8754 gold badges17 silver badges25 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 2

For new ers, as for now, there is no dataTransfer for touch events (yet?). You can do the same functionality as DnD with abit more work. DnD just simplify the data transfer process and handle couple of stuff like detecting dragenter, but you can have the same with touch it's just you have to do all the dragenter detection stuff by yourself.

On touch start, I stored the reference of the drag element into variable, this is similar to dataTransfer.setData(), but added work here is to simulate the feeling of drag and drop by duplicating a new element to follow your touch event

  function dragTouchstart(e){
//global variable that store the touch/drag element
  imgId = e.target
    let image = document.createElement("img"); // Create a new element
  image.setAttribute("id", "image-float");


  // get the image from the stored reference
  image.src =imgId.src
  image.width = 100
  image.height = 100

  // position the image to the touch, can be improve to detect the position of touch inside the image
  let left = e.touches[0].pageX;
  let top = e.touches[0].pageY;
  image.style.position = 'absolute'
  image.style.left = left + 'px';
  image.style.top = top + 'px';
  image.style.opacity = 0.5;


  document.body.appendChild(image);  
  }

On touchmove, this is purely to simulate the dragging feeling that you get from dnd, get the element created in touchstart and make it follow your touchmove. but I also add the touchenter function to detect the dragenter

   function dragTouchmove(e) {
  // on touch move or dragging, we get the newly created image element
  let image = document.getElementById('image-float')
  // this will give us the dragging feeling of the element while actually it's a different element
  let left = e.touches[0].pageX;
  let top = e.touches[0].pageY;
  image.style.position = 'absolute'
  image.style.left = left + 'px';
  image.style.top = top + 'px';
  let touchX = e.touches[0].pageX
  let touchY = e.touches[0].pageY
  //apply touch enter fucntion inside touch move
  dragTouchenter(e,touchX,touchY)
  }

my touchenter function

  function dragTouchenter(e,touchX,touchY){
  let one =document.getElementById('1')
  let two =document.getElementById('2')
  let three =document.getElementById('3')
  
  let id1 = one.getBoundingClientRect();
  let id2 = two.getBoundingClientRect();
  let id3 = three.getBoundingClientRect();
  
    // to detect the overlap of touchmove with dropzone
  var overlap1 = !(id1.right < touchX ||
    id1.left > touchX ||
    id1.bottom < touchY ||
    id1.top > touchY)
    
  var overlap2 = !(id2.right < touchX ||
    id2.left > touchX ||
    id2.bottom < touchY ||
    id2.top > touchY)
    
     var overlap3 = !(id3.right < touchX ||
    id3.left > touchX ||
    id3.bottom < touchY ||
    id3.top > touchY)
    
    //detect touchenter then apply style, if false, equal to touchleave
    //could write a better function to take these elements in array and apply function accordingly
    //but as for now I just write like this because faster by copy+paste
    //get dropZoneId too
    if(overlap1){
     one.style.border = "dotted";
    one.style.borderColor = "#0b79d0";
    dropZoneId =one
    }else{
    one.style.border = "1px solid #0b79d0";
    }
    
    if(overlap2){
     two.style.border = "dotted";
    two.style.borderColor = "#0b79d0";    
    dropZoneId =two
    }else{
    two.style.border = "1px solid #0b79d0";
    }
    
    if(overlap3){
     three.style.border = "dotted";
    three.style.borderColor = "#0b79d0";
    dropZoneId =three
    }else{
    three.style.border = "1px solid #0b79d0";
    }
    
    if(!overlap1 && !overlap2 && !overlap3){
    dropZoneId = ''
    }
    
    /* console.log(dropZoneId.id) */
  }

finally ontouchend will do all the logic that you will do with dataTransfer.getData()

  function dragTouchend(e){
  //remove dragged image duplicate
    let image = document.getElementById('image-float')
    image.remove()
    
    dropZoneId.style.border = "1px solid #0b79d0";
    //if outside any dropzone, just do nothing
    if(dropZoneId == '') {
    dropZoneId = ''
    imgId = ''
    }else{
    // if inside dropzone, swap the image 
    let toSwap = dropZoneId.children[0]
    let originDropzone= imgId.parentElement
    originDropzone.appendChild(toSwap)
    dropZoneId.appendChild(imgId)
    
    dropZoneId = ''
    imgId = ''
    
    }
  }

full working example based on OP is down below, changed an image because it appear to be broken

div {
  display: inline-block;
  border: 1px solid #0b79d0;
}

div,
img {
  width: 120px;
  height: 120px;
}
<div id="1" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
  <img id="a" draggable="true" ondragstart="dragStart(event)" src="https://static.webshopapp./shops/073933/files/156288269/345x345x1/artibalta-white-tiger.jpg" ontouchstart="dragTouchstart(event)" ontouchmove="dragTouchmove(event)" ontouchend="dragTouchend(event)"
  />
</div>

<div id="2" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
  <img id="b" draggable="true" ondragstart="dragStart(event)" src="https://yt3.ggpht./a-/AN66SAyfsmao4f1EEOqkBP2PgpSUcabPJXLZ1sLEnA=s288-mo-c-c0xffffffff-rj-k-no" ontouchstart="dragTouchstart(event)" ontouchmove="dragTouchmove(event)" ontouchend="dragTouchend(event)"
  />
</div>

<div id="3" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
  <img id="c" draggable="true" ondragstart="dragStart(event)" src="https://cdn.quasar.dev/img/avatar1.jpg" ontouchstart="dragTouchstart(event)" ontouchmove="dragTouchmove(event)" ontouchend="dragTouchend(event)" />
</div>

<script>
  var imgId = 'test'
  var dropZoneId = ''

  function allowDrop(ev) {
    ev.preventDefault();
  }

  function dragTouchstart(e) {
    imgId = e.target
    let image = document.createElement("img"); // Create a new element
    image.setAttribute("id", "image-float");


    // get the image from the stored reference
    image.src = imgId.src
    image.width = 100
    image.height = 100

    // position the image to the touch, can be improve to detect the position of touch inside the image
    let left = e.touches[0].pageX;
    let top = e.touches[0].pageY;
    image.style.position = 'absolute'
    image.style.left = left + 'px';
    image.style.top = top + 'px';
    image.style.opacity = 0.5;


    document.body.appendChild(image);
  }

  function dragTouchmove(e) {
    // on touch move or dragging, we get the newly created image element
    let image = document.getElementById('image-float')
    // this will give us the dragging feeling of the element while actually it's a different element
    let left = e.touches[0].pageX;
    let top = e.touches[0].pageY;
    image.style.position = 'absolute'
    image.style.left = left + 'px';
    image.style.top = top + 'px';
    let touchX = e.touches[0].pageX
    let touchY = e.touches[0].pageY
    //apply touch enter fucntion inside touch move
    dragTouchenter(e, touchX, touchY)
  }

  function dragTouchenter(e, touchX, touchY) {
    let one = document.getElementById('1')
    let two = document.getElementById('2')
    let three = document.getElementById('3')

    let id1 = one.getBoundingClientRect();
    let id2 = two.getBoundingClientRect();
    let id3 = three.getBoundingClientRect();

    // to detect the overlap of touchmove with dropzone
    var overlap1 = !(id1.right < touchX ||
      id1.left > touchX ||
      id1.bottom < touchY ||
      id1.top > touchY)

    var overlap2 = !(id2.right < touchX ||
      id2.left > touchX ||
      id2.bottom < touchY ||
      id2.top > touchY)

    var overlap3 = !(id3.right < touchX ||
      id3.left > touchX ||
      id3.bottom < touchY ||
      id3.top > touchY)

    //detect touchenter then apply style, if false, equal to touchleave
    //could write a better function to take these elements in array and apply function accordingly
    //but as for now I just write like this because faster by copy+paste
    //get dropZoneId too
    if (overlap1) {
      one.style.border = "dotted";
      one.style.borderColor = "#0b79d0";
      dropZoneId = one
    } else {
      one.style.border = "1px solid #0b79d0";
    }

    if (overlap2) {
      two.style.border = "dotted";
      two.style.borderColor = "#0b79d0";
      dropZoneId = two
    } else {
      two.style.border = "1px solid #0b79d0";
    }

    if (overlap3) {
      three.style.border = "dotted";
      three.style.borderColor = "#0b79d0";
      dropZoneId = three
    } else {
      three.style.border = "1px solid #0b79d0";
    }

    if (!overlap1 && !overlap2 && !overlap3) {
      dropZoneId = ''
    }

    /* console.log(dropZoneId.id) */
  }

  function dragTouchend(e) {
    //remove dragged image duplicate
    let image = document.getElementById('image-float')
    image.remove()

    dropZoneId.style.border = "1px solid #0b79d0";
    //if outside any dropzone, just do nothing
    if (dropZoneId == '') {
      dropZoneId = ''
      imgId = ''
    } else {
      // if inside dropzone, swap the image 
      let toSwap = dropZoneId.children[0]
      let originDropzone = imgId.parentElement
      originDropzone.appendChild(toSwap)
      dropZoneId.appendChild(imgId)

      dropZoneId = ''
      imgId = ''

    }
  }

  function dragEnter(ev) {
    var element = document.getElementById(ev.target.id);
    element.style.border = "dotted";
    element.style.borderColor = "#0b79d0";
  }

  function dragLeave(ev) {
    var element = document.getElementById(ev.target.id);
    element.style.border = "1px solid #0b79d0";
  }

  function dragStart(ev) {
    ev.dataTransfer.setData("src", ev.target.id);
    var number = ev.target.id.replace(/[^\d.]/g, '');
    ev.dataTransfer.setData("text/plain", number);
  }

  function drop(ev) {
    ev.preventDefault();
    var src = document.getElementById(ev.dataTransfer.getData("src"));

    var srcParent = src.parentNode;
    var tgt = ev.currentTarget.firstElementChild;
    ev.currentTarget.replaceChild(src, tgt);
    srcParent.appendChild(tgt);

    var number1 = srcParent.id.replace(/[^\d.]/g, '');
    var number2 = ev.currentTarget.id.replace(/[^\d.]/g, '');

    var element = document.getElementById(ev.target.id);
    element.style.border = "solid 1px #0b79d0";

    var number = ev.target.id.replace(/[^\d.]/g, '');
  }
</script>

I strongly remend you to use a library to do that in mobile. I had to do relatively same behaviour and i used interact.js which is very handful and mobile ready.

If you just want to add touch gestures to your app, just look at Hammer.js. It is the main library to use touch events.

And if you use jQuery look at this topic.

I hope that helps.

  • Way to solve it some time ago (if time was main factor) was to implement touch punch - http://touchpunch.furf./

  • It's jquery-ui plugin that wires touch events to mouse events (so usually importing lib is enough - no need to change your code)

For educational purpose it's good what you're doing.

But for development it's better not reinvent the wheel and being capable of use a tool, that can help you over time.

So i remend you this Drag & Drop library. Easy to use, fun, lightweight and very well maintained.

发布评论

评论列表(0)

  1. 暂无评论