Click here to Skip to main content
15,883,883 members
Articles / jQuery-UI

Drag and Drop when jQuery-ui draggable Fails on Mobile Browsers!

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
27 Oct 2016CPOL4 min read 22.1K   2   8
This article describes yet another drag and drop approach that can be used when the jQuery-ui and touch-punch library fails (when the project uses transform-origin or other coordinate transformations)

Introduction

This is my fourth article on CodeProject and I hope that you will can appreciate it because in the following paragraphs, I would like to describe and handle a drawback that only appears a few times in the life of a web developer!

jQuery, jQuery-ui and touch-pouch library can be considered the standard de facto for drag and drop operations in desktop and mobile browser. This because these three libraries permit ignoring all the complexity of a drag and drop operation, just by calling one method: $.draggable() that informs the jQuery subsystem that an object can be dragged.

The $.draggable method can be decorated with drop: function (event,ui) {} parameter in which we can add the code for handling the drop event.

This seems perfect! But, what happens when we use some new CSS3 features?

Background

When we are working in high responsive environment, we could use transformations like transform-coordinate, transform:scale(), transform:rotate and so forth. It seems that jQuery does not like this occurrence very much and, in some cases, it can make a mistake in some actions.

For this article, I have chosen HYPE3 and TUMULT that permits the handling re-scaling and re-sizing in an automatic way. I prepared two examples in order to show the mistakes.

In the following pictures, you can view the same objects resized and rescaled on different devices by HYPE engine without any user codes.

Desktop Browser View

Image 1

Iphone Landscape and Portrait Views

Image 2Image 3

As you can see, all three views seem to be the same (rescaled referred to viewport size).

But, I would like to invite you to play demo1 on different browsers. You can experiment that the drag and drop will fail because drag revert will mistake the return coordinates in landscape and desktop browser (the original position will not be respected).

I am not sure about the recurrence of this kind of problem, but I can confirm that this issue appears when the coordinate transformation function is used. Note that DEMO1 only uses jQuery, jQuery-ui and touch-pouch libraries and not the native drag and drop support.

Using the Code

I think that the draggable revert issues will lie in jQuery-ui implementation but I'm not sure about this. So, my idea was to trash jQuery-ui and pouch-touch libraries and to use:

  • Native Desktop browser drag and drop (Chrome, Firefox, Opera, Safari and Edge support it)
  • Touch-events for mobile browser drag and drop capability

Desktop Browser Native Support

The first step for this kind of approach must be the assignment of a class or unique-id to each object which we want to made draggable. In my example, all colored squares were named c1, c2... c12 and have the class name gameObject. So, I just enable the native drag function with following code.

JavaScript
for (var c=1;c<=12;c++)
{
    var e = document.getElementById("c"+c);
    $("#c"+c).attr("draggable","true");
    $("#c"+c).attr("ondragstart","_dragStart(event)");
    $("#c"+c).attr("ondrop","_onDrop(event)");
    $("#c"+c).attr("ondragover","_onDragOver(event)");
}

For simplicity, I have reported the three functions that handle drag and drop in separated code blocks

JavaScript
function _dragStart(a)
{
    a.dataTransfer.setData("text", a.target.id);
}

function _onDrop(e)
{
  e.preventDefault();
  var src = e.dataTransfer.getData("text");
  var dst = e.target.id;
  // here you can do what you want with source and destination of drag and drop !
}

 function _onDragOver(a)
 {
   a.preventDefault();
 }

Mobile Browser Drag and Drop with Touches

Nowadays, mobile browsers do not implement a native support for drag and drop. So, the second step was to handle the three gestures that represents a drag and drop action on a touchable screen: touchstart, touchmove and touchend.

All drag and drop operations use a shadow (aka ghost) image that helps the actions. This image will be shown near the cursor in order to show the user which objects are moving. We also need to track the start position of an object in order to revert drag and drop.

The following three variables can help us with touches! You can try DEMO2 in order to try this kind of approach.

JavaScript
// touch capability
var startPos=[];
var toDrag = null;
var dragStart = false;

Then, for each touch on screen, we need to understand if the user hits a draggable object. This can be done by simple collision detection.

JavaScript
$(document).on("touchstart",function(e) {
   e.preventDefault();
   // touch coordinates
   var xPos = e.originalEvent.touches[0].pageX;
   var yPos = e.originalEvent.touches[0].pageY;

   startPos[0]=xPos;
   startPos[1]=yPos;

   $(".gameobject").each(function(index)
   {
       var jx = parseFloat($(this).offset().left);
       var jy = parseFloat($(this).offset().top);
       var jw = parseFloat($(this).width());
       var jh = parseFloat($(this).height());

       //collision detect
       if (jx <= xPos && jy <= yPos && xPos <= (jx+jw) && yPos <= (jy+jh))
       {
          if ($(this).attr("draggable")=="false")
          {
            //object touched is not draggable!
          }
          else {
               // collision detected
               toDrag = $(this).attr("id");
               dragStart=true;
               // setting up shadow image
               var shadow = document.getElementById("shadow");
               if (shadow==null)
               {
                   shadow = document.createElement("div");
                   document.getElementById("game").appendChild(shadow);
                   shadow.setAttribute("id","shadow");
                   shadow.style.position="absolute";
                   shadow.style.zIndex="9999999";
               }
                  // for the purpose of this article ghost image will be a square of
                  // the same color of touched object
                  $("#shadow").css("visibility","visible");
                  var shadowImg = $(this).css("background-image");
                  $("#shadow").css("background-image",shadowImg);
                  $("#shadow").css("background-size","100% 100%");
                  $("#shadow").css("left",xPos+"px");
                  $("#shadow").css("top",yPos+"px");
                  $("#shadow").css("width",jw+"px");
                  $("#shadow").css("height",jh+"px");
                  return;
             }
          }
        });
      });

Now, we detected a collision on draggable object and therefore we need to track the finger of the user by following any other touches. This can be done with a few lines of code.

JavaScript
$(document).on("touchmove",function(e) {
    e.preventDefault();
    if (dragStart && toDrag!=null)
    {
          // move the shadow
          var xPos = e.originalEvent.touches[0].pageX;
          var yPos = e.originalEvent.touches[0].pageY;
          var sw = parseFloat($("#shadow").width());
          var sh = parseFloat($("#shadow").height());
          $("#shadow").css("left",(xPos-sw/2)+"px");
          $("#shadow").css("top",(yPos-sh/2)+"px");
     }
});

Finally, we need to understand where the finger leaves the screen. This will be the last coordinates of our shadow images and so the drop position.

JavaScript
  $(document).on("touchend",function(e) {
   e.preventDefault();
   if (dragStart && toDrag!=null)
   {
         //here you can apply graphics effect on shadow like revert to initial pos or fade out
         $("#shadow").css("visibility","hidden");
         dragStart=false;

         var xPos = 0;
         var yPos = 0;

         if (e.changedTouched==undefined)
         {
                xPos = e.originalEvent.changedTouches[0].pageX;
                yPos = e.originalEvent.changedTouches[0].pageY;
         }
         else
         {
                xPos = e.changedTouches[0].pageX;
                yPos = e.changedTouched[0].pageY;
         }
         // looking for collision between gameObject and touchleave position
         var target = null;
         $(".gameobject").each(function(index) {
           var tx = parseFloat($(this).offset().left);
           var ty = parseFloat($(this).offset().top);
           var tw = parseFloat($(this).width());
           var th = parseFloat($(this).height());

           if (tx <= xPos && ty <= yPos && xPos <= (tx+tw) && yPos <= (ty+th))
           {
               target = $(this).attr("id");
               //here you can execute code on source and target
               return;
           }
           }).promise().done(function() { });
           }
     });
}

Points of Interest

This article could be useless if anyone knows a jQuery-ui configuration parameter that fixes this problem. I don't know any method, but I'm not sure whether one exists.

History

  • Improving English (Thx to Natascia Spada) @ 27/10/2016 - 12:18
  • Published @ 25/10/2016 - 15:00

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer BiTS - Softmining
Italy Italy
Luigi Di Biasi is a PhD student in a computer science @ University of Salerno.

He is sole director of Arrowsoft.IT SRLs and CIO of Softmining SRL. He is founder of dibiasi.it materiale elettrico.

He studied Computer Science at the University of Salerno in 2013. From 2014 to 2016 he is [assegnista di ricerca] for PRIN 2010-2011 Data-Centric Genomic Computing (GenData 2020)).

He collaborate to the GRIMD developing (grid for molecular dynamics), which permits the parallel and distributed computation of many kinds of scientific software, to the YADA developing (Yet Another Docking Approach), a tool that improve and refine the predictions made by VINA Autodock and to the implementation of ProtComp alignment free tools, a new algorithm for sequences analysis that outperforms existing methods. He collaborate on 7 scientific paper.

In 2017 he is responsible developer for the RIRIBOX Project (in collaboration with Acea Pinerolese and Ecofficina SRL) and for energii.dk (in collaboration with Peme IVS). All these projects has involved collaborations between Italian, Danish and American developers.

In 2016 he is responsible developer for the “Una Buona Occasione” Project (in collaboration with Ecofficina SRL). He developed all the edutainment games related to this project (16 WebGL games).

Actually, his research activities range on the AI (algorithms developing, applications) and on the game developing.

Comments and Discussions

 
QuestionComplete Example Pin
Dewey27-Oct-16 8:25
Dewey27-Oct-16 8:25 
AnswerRe: Complete Example Pin
luigidibiasi27-Oct-16 8:52
luigidibiasi27-Oct-16 8:52 
AnswerHave you tried TouchPunch? Pin
Derek Wade26-Oct-16 9:11
professionalDerek Wade26-Oct-16 9:11 
GeneralRe: Have you tried TouchPunch? Pin
luigidibiasi26-Oct-16 13:11
luigidibiasi26-Oct-16 13:11 
GeneralRe: Have you tried TouchPunch? Pin
Derek Wade26-Oct-16 13:30
professionalDerek Wade26-Oct-16 13:30 
I do admit I needed to fix a few issues with it, but overall it worked to simulate the mouse events for touch.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.