126

I am looking for a drag & DROP plugin that works on touch devices.

I would like similar functionality to the jQuery UI plugin which allows "droppable" elements.

The jqtouch plugin supports dragging, but no dropping.

Here is drag & drop that only supports iPhone/iPad.

Can anyone point me in the direction of a drag & drop plugin that works on android/ios?

...Or it might be possible to update the jqtouch plugin for droppability, it already runs on Andriod and IOS.

Thanks!

neshpro9
  • 433
  • 3
  • 17
joe
  • 16,988
  • 36
  • 94
  • 131
  • 1
    This is in progress, but I don't know what devices support it: This http://plugins.jquery.com/project/mobiledraganddrop ... have you looked through the jQuerymobile.com libraries? I also found this duplicate SO question: http://stackoverflow.com/questions/3172100/html-drag-and-drop-on-mobile-devices – iivel Mar 03 '11 at 21:11
  • Thanks. Yeah mobiledraganddrop only supports click and drop on mobile devices. I could not find anything on jQuerymobile.com. The other question has an answer already marked as correct that refers to http://www.sencha.com/products/touch/ which has a limited source licence. – joe Mar 03 '11 at 21:28
  • I'm curious for one that works on mobile and desktop browsers. – Lime Jun 15 '11 at 18:34

5 Answers5

268

You can use the Jquery UI for drag and drop with an additional library that translates mouse events into touch which is what you need, the library I recommend is https://github.com/furf/jquery-ui-touch-punch, with this your drag and drop from Jquery UI should work on touch devises

or you can use this code which I am using, it also converts mouse events into touch and it works like magic.

function touchHandler(event) {
    var touch = event.changedTouches[0];

    var simulatedEvent = document.createEvent("MouseEvent");
        simulatedEvent.initMouseEvent({
        touchstart: "mousedown",
        touchmove: "mousemove",
        touchend: "mouseup"
    }[event.type], true, true, window, 1,
        touch.screenX, touch.screenY,
        touch.clientX, touch.clientY, false,
        false, false, false, 0, null);

    touch.target.dispatchEvent(simulatedEvent);
    event.preventDefault();
}

function init() {
    document.addEventListener("touchstart", touchHandler, true);
    document.addEventListener("touchmove", touchHandler, true);
    document.addEventListener("touchend", touchHandler, true);
    document.addEventListener("touchcancel", touchHandler, true);
}

And in your document.ready just call the init() function

code found from Here

kiplantt
  • 323
  • 1
  • 7
ryuutatsuo
  • 3,924
  • 3
  • 27
  • 29
  • Awesome! The only thing, with jquery mobile, you'll want to bind to .live("pagecreate") over document.ready. – Daniel D Aug 26 '11 at 07:22
  • 11
    Thanks, on my Android device stuff is now draggable. However, the click event is not fired anymore when I click on it. Any ideas how to fix that? – John Landheer Dec 02 '11 at 08:58
  • 9
    Note: Since the event listener is registered for the whole document, this solution also disables scrolling on my Nexus S... – Ethan Leroy May 10 '12 at 12:12
  • 30
    Thanks for sharing this. However, you should give credit to the original author of the code you have posted. http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/ – undefined May 18 '12 at 00:22
  • Wrapping the event.preventDefault() within an if statement so that it only fires on certain events can help if you are getting secondary blocking of events, e.g. only firing on mousemove if using jQuery UI sortable prevents scrolling after the sort has been destroyed. – notreadbyhumans Oct 20 '12 at 16:16
  • 3
    @JohnLandheer I fixed the issue by attaching the event listeners only to the objects I wanted draggable, `document.getElementById('draggableContainer').addEventListener(...` – chiliNUT Nov 05 '13 at 21:34
  • 1
    Touch-punch is great. One of those 'it just works' things. Alas, it doesn't support dual-touch...which I'd love to see. – DA. Jan 20 '14 at 17:55
  • How do you bind init(); to a specific object instead of document.ready? – Sue May 28 '14 at 15:30
  • this does not seem to work with surface (see http://jsfiddle.net/8eogp1d2/) and i don't have any other touch device to try. – morpheus Aug 08 '14 at 23:11
  • @morpheus there is a certain css that needs to be added to the surface if I remember correctly for touch events to work properly. I think it is -ms-touch-action: none; Not sure though I don't have a surface. – ryuutatsuo Aug 09 '14 at 04:09
  • yes that's right. and for surface it seems that is the only thing one needs to do i.e., no need for the js in this answer. – morpheus Aug 09 '14 at 16:04
  • can anyone explain what is the purpose of event.preventDefault in this script? i am running into same problems reported by gregpress, erwinus and eric - the touchHandler method is reliably hit and the simulatedEvent is dispatched but the handlers that should respond to this event are not getting reliably hit on my android device. i tried commenting out event.preventDefault but the problem remains. on surface i am not seeing any issue. – morpheus Aug 31 '14 at 19:34
  • investigated above. it has to do with the fact that the target of the touch event is not the element that has the mouse event handler attached to it e.g., i attached mouse event handler to an img but the target of the touch event is html. as a result the mouse handler never gets called when dispatchEvent is called on html – morpheus Aug 31 '14 at 22:09
  • Is there any way to get this working with dragstart and drop events? I've tried replacing mousedown with dragstart and mouseup with drop and it doesn't pick up on the dataTransfer.setData – Jimmy T Sep 30 '14 at 12:23
  • Not sure why this has so many upvotes when there are obviously so many problems with it. It breaks scrolling and all click events on my Android device. – Vincent Feb 09 '15 at 17:36
  • Great answer, but i wan to know how to add Click event ? – Jothi Kannan Nov 02 '15 at 09:02
  • It should be downvoted as it disables click events on your mobile device – Otvazhnii Aug 03 '17 at 17:12
  • @joe, used jquery.ui.touch-punch.min.js and solve the problem. This jquery works both android and ipad. – Pradosh Mukhopadhayay Feb 13 '18 at 18:53
  • 1
    Touchpunch is outdated and don't works with newer versions of jQuery UI anymore. So this solution is now gone. – Mr. Jo Jun 04 '19 at 11:14
21

For anyone looking to use this and keep the 'click' functionality (as John Landheer mentions in his comment), you can do it with just a couple of modifications:

Add a couple of globals:

var clickms = 100;
var lastTouchDown = -1;

Then modify the switch statement from the original to this:

var d = new Date();
switch(event.type)
{
    case "touchstart": type = "mousedown"; lastTouchDown = d.getTime(); break;
    case "touchmove": type="mousemove"; lastTouchDown = -1; break;        
    case "touchend": if(lastTouchDown > -1 && (d.getTime() - lastTouchDown) < clickms){lastTouchDown = -1; type="click"; break;} type="mouseup"; break;
    default: return;
}

You may want to adjust 'clickms' to your tastes. Basically it's just watching for a 'touchstart' followed quickly by a 'touchend' to simulate a click.

E.Z. Hart
  • 5,717
  • 1
  • 30
  • 24
  • @E. Z. Hart - could you please let me know where to put your code in above given code to enable click event ??? – Valay Nov 12 '14 at 07:05
  • 1
    @ChakoDesai - The code in the answer has been edited heavily since I wrote my response. Check out http://stackoverflow.com/posts/6362527/revisions and look at the code from June 30, 2011; my answer will make a lot more sense based on that version. – E.Z. Hart Nov 13 '14 at 02:13
  • @E. Z. Hart - I've updated my code from the given url. But why sometimes `click` works and sometimes not ??? – Valay Nov 13 '14 at 09:38
  • @ChakoDesai - Without seeing your code I can't be sure. You might want to open a new question for that. – E.Z. Hart Nov 13 '14 at 22:04
  • An updated answer to make the above code work with click events can be found here: http://stackoverflow.com/questions/28218888/touch-event-handler-overrides-click-handlers – Blunderfest Feb 02 '15 at 15:03
12

Thanks for the above codes! - I tried several options and this was the ticket. I had problems in that preventDefault was preventing scrolling on the ipad - I am now testing for draggable items and it works great so far.

if (event.target.id == 'draggable_item' ) {
    event.preventDefault();
}
gregpress
  • 143
  • 1
  • 8
  • Have you tested this in Windows Phone? Or anything other than iPhone? From past experience, this seems to work perfect in iPhones/iPads, but doesn't work well in other devices, specially in Windows Phones. – almost a beginner Nov 25 '16 at 04:49
10

I had the same solution as gregpress answer, but my draggable items used a class instead of an id. It seems to work.

var $target = $(event.target);  
if( $target.hasClass('draggable') ) {  
    event.preventDefault();  
}
random_user_name
  • 25,694
  • 7
  • 76
  • 115
Eric
  • 175
  • 2
  • 2
  • 4
    The fact that you have simply used a different selector does not warrant being a separate answer, but instead you should have commented on @gregpress's answer. – bPratik Sep 28 '12 at 23:55
  • 2
    @bPratik unless he wanted the nicer formatting an answer provides -- and the fact that he provided an entire code example does warrant a separate answer. Should have left a comment linking to his answer though... – drzaus Sep 03 '13 at 18:34
8

Old thread I know.......

Problem with the answer of @ryuutatsuo is that it blocks also any input or other element that has to react on 'clicks' (for example inputs), so i wrote this solution. This solution made it possible to use any existing drag and drop library that is based upon mousedown, mousemove and mouseup events on any touch device (or cumputer). This is also a cross-browser solution.

I have tested in on several devices and it works fast (in combination with the drag and drop feature of ThreeDubMedia (see also http://threedubmedia.com/code/event/drag)). It is a jQuery solution so you can use it only with jQuery libs. I have used jQuery 1.5.1 for it because some newer functions don't work properly with IE9 and above (not tested with newer versions of jQuery).

Before you add any drag or drop operation to an event you have to call this function first:

simulateTouchEvents(<object>);

You can also block all components/children for input or to speed up event handling by using the following syntax:

simulateTouchEvents(<object>, true); // ignore events on childs

Here is the code i wrote. I used some nice tricks to speed up evaluating things (see code).

function simulateTouchEvents(oo,bIgnoreChilds)
{
 if( !$(oo)[0] )
  { return false; }

 if( !window.__touchTypes )
 {
   window.__touchTypes  = {touchstart:'mousedown',touchmove:'mousemove',touchend:'mouseup'};
   window.__touchInputs = {INPUT:1,TEXTAREA:1,SELECT:1,OPTION:1,'input':1,'textarea':1,'select':1,'option':1};
 }

$(oo).bind('touchstart touchmove touchend', function(ev)
{
    var bSame = (ev.target == this);
    if( bIgnoreChilds && !bSame )
     { return; }

    var b = (!bSame && ev.target.__ajqmeclk), // Get if object is already tested or input type
        e = ev.originalEvent;
    if( b === true || !e.touches || e.touches.length > 1 || !window.__touchTypes[e.type]  )
     { return; } //allow multi-touch gestures to work

    var oEv = ( !bSame && typeof b != 'boolean')?$(ev.target).data('events'):false,
        b = (!bSame)?(ev.target.__ajqmeclk = oEv?(oEv['click'] || oEv['mousedown'] || oEv['mouseup'] || oEv['mousemove']):false ):false;

    if( b || window.__touchInputs[ev.target.tagName] )
     { return; } //allow default clicks to work (and on inputs)

    // https://developer.mozilla.org/en/DOM/event.initMouseEvent for API
    var touch = e.changedTouches[0], newEvent = document.createEvent("MouseEvent");
    newEvent.initMouseEvent(window.__touchTypes[e.type], true, true, window, 1,
            touch.screenX, touch.screenY,
            touch.clientX, touch.clientY, false,
            false, false, false, 0, null);

    touch.target.dispatchEvent(newEvent);
    e.preventDefault();
    ev.stopImmediatePropagation();
    ev.stopPropagation();
    ev.preventDefault();
});
 return true;
}; 

What it does: At first, it translates single touch events into mouse events. It checks if an event is caused by an element on/in the element that must be dragged around. If it is an input element like input, textarea etc, it skips the translation, or if a standard mouse event is attached to it it will also skip a translation.

Result: Every element on a draggable element is still working.

Happy coding, greetz, Erwin Haantjes

Codebeat
  • 6,501
  • 6
  • 57
  • 99
  • which object should I pass in `simulateTouchEvents(, true);` ??? – Valay Nov 12 '14 at 07:25
  • For example: simulateTouchEvents( $('#yourid; ), true ); or simulateTouchEvents( document.getElementById('yourid') , true ); etc – Codebeat Nov 13 '14 at 00:33
  • could you please look at my another question [link](http://stackoverflow.com/questions/26906479/jquery-how-to-enable-scrolling-on-touch-devices) ??? – Valay Nov 17 '14 at 10:15
  • Use http://threedubmedia.com/code/event/drag and disable x or y moving of element. When you use the code in this article you can 'scroll' the scrollbars. But do know why you want to do this because a page on mobile phone got already scrollbars. Don't understand what you want to do with it. Success! – Codebeat Nov 17 '14 at 10:54
  • I've to add drag-n-drop of columns and scrolling both. When I turn on `touch` events in `chrome`, I am not able to scroll the table. Not even on mobile. – Valay Nov 17 '14 at 11:02