Simple drag ‘n drop Javascript for iPhone and iPad

With smart phones everywhere and tablets popping up at an increasing speed I just thought that I could share this quick tutorial on how to make touch screen devices interact with the web. In other words: capturing touch screen events on your web page.

In this post I’ll show you how to make an object draggable on your iPhone/iPad/Android device (might work on other touch devices with decent browsers, just haven’t tested any other). As usual when dealing with web and interaction we’re going to use javascript.

Now, traditionally when handling mouse-events we can listen to either mousemove, mousedown, mouseup or onclick. However, these won’t work in your shiny new touch device. Instead we need to bind our function to the events touchstart, touchmove, touchend or touchcancel

Once you have an event handler registered, there are three ways of getting the touch parameters (e is the argument of the handler function):

e.touches – all the touches on a page

e.targetTouches – get all touches for the target element

e.changedTouches – changed touches for this event

These event properties will be arrays, so f.ex. to get the first touch event you would write something like this:

1

2

3

document.addEventListener("touchstart",function(e){

vartouch=e.changedTouches[0];

},false);

This is nice, because it allows you to handle multi touch aswell. Just check event.touches.length to get the number of touch events. However, pursuing our goal, to get the X and Y coordinates of the touch event we use the attributes pageX and pageY:

I’ll be using jQuery for simple event listener registration. There is, however, one pitfall when using it:
normally when adding event listeners using the addEventListener method you would get the browser event passed as the first argument to your handler. With jQuery, you get a special jQuery event, so in order to find the touch parameters you’ll have to access the original browser event using e.originalEvent.

So, the code then:

1

2

3

4

5

6

7

8

9

$(document).bind("touchstart touchmove",function(e){

//Disable scrolling by preventing default touch behaviour

e.preventDefault();

varorig=e.originalEvent;

varx=orig.changedTouches[0].pageX;

vary=orig.changedTouches[0].pageY;

// Move a div with id "rect"

$("#rect").css({top:y,left:x});

});

The code above will try to move an object with id “rect”, so just add the appropriate HTML/CSS and you’re done

Improving

Instead of binding the event handlers to ‘document’ we can bind them directly to the element that is going to be draggable. This way we allow multiple objects to be draggable simultaneously. Now, the code will like like this instead:

1

2

3

4

5

6

7

8

9

varmoveMe=function(e){

e.preventDefault();

varorig=e.originalEvent;

$(this).css({

top:orig.changedTouches[0].pageY,

left:orig.changedTouches[0].pageX

});

};

$(".draggable").bind("touchstart touchmove",moveMe);

Since ‘this’ will be bound to the touched DOM-element when the event fires, we can use that instead of hardcoding the element that is going to be moved like I did in the previous code. This way you only need to eg. assign a certain class, ‘draggable’, to the element that is going to be draggable. Neat!

Make it a plugin and take care of offset

As a final touch, we can go ahead and wrap the functionality into a jQuery plugin, making objects draggable by just invoking a function on the jQuery set that has been selected. Also, as Ronald Harris pointed out here in the comments, the previous approach only works for elements which has a parent that is positioned statically. So we need to calculate the elements offset at touchstart and compensate for it while dragging. Here’s the code:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

$.fn.draggable=function(){

varoffset=null;

varstart=function(e){

varorig=e.originalEvent;

varpos=$(this).position();

offset={

x:orig.changedTouches[0].pageX-pos.left,

y:orig.changedTouches[0].pageY-pos.top

};

};

varmoveMe=function(e){

e.preventDefault();

varorig=e.originalEvent;

$(this).css({

top:orig.changedTouches[0].pageY-offset.y,

left:orig.changedTouches[0].pageX-offset.x

});

};

this.bind("touchstart",start);

this.bind("touchmove",moveMe);

};

$(".draggable").draggable();

Demo

See the final demo at: http://sewa.se/drag/
Please note that the page will be extremely boring when accessed using your PC. Use your iPhone, Android phone, iPad or similar

Awesome! This functionality should be made into an iPhone/iPad-plugin for jQuery

antonio

Nice example, but i think it’s going to get old pretty fast.
Check out this new jQuery initiative: http://jquerymobile.com/

Also, there’s a simple way to achieve this with almost all classes without writing the object reference (as you’ve done with “#rect”) using jQuery draggables: http://dev.jsui.de/test/ipad.html

http://sewa.se Sebastian Wallin

Hi!

Thanks for the links! Great inspiration. I really like jQuery mobile (although I’m suffering a great deal from the classical “not invented here” syndrome =)).

The point with my post was more to show which events are triggered by the browser during touch interaction, than to create a structured way of handling them =) (hence the dirty code). That’s where the great libraries come in. My apologies if the post was misleading

I’ve now added the section ‘Improving’ to the article, explaining how to do it. Thanks for making me think of it =)

Amit hooda

cool thnks 4 the article ,,nthng els was wrking out ,,,,

Amit hooda

heyy css of your demo you hav assigned larginleft and top as the same,,,but what if i want the draggable item to be some specific co-ordinates,,,can u help with it???

Ken Chow

Thanks for sharing this article. One question though – how would you make a droppable area work when you drag an object into it?

Amit Hooda

@google-c0e7f3cd91299033ba1e3b641494bc57:disqus You can simply use touchend event and check the co-ordinates at touchend event if they lie in the droppable area than u can make the item that is dragged fadeout ,,,

Ken Chow

Thanks for the reply Amit.

However, wouldn’t the co-ordinates change if the user rotates the iphone/ipad?

Ed Walker

Hey great article! I want to create a tray that I can drag out into the page. How would I restrict to a certain axis and also restrict to an area (so you can drag the tray out and it just comes a certain amount out)?

http://www.facebook.com/PandasAndJaguars Ronald Harris

Very nice. One question. Dragging does not work if the parent or ancestor’s position definition is set to anything other than “static”. I’m having problems nesting this in a positioned container. Any suggestions?

http://sewa.se Sebastian Wallin

Good point! I’ve added a section in the post where I calculate the offset at touchstart and compensating for it while dragging. It should do the trick!

Konstantin

What the script shows is more a move function than drag & drop. Drag & Drop works with drag source and target.

When I put this into my code and test on the iPad, every time I touch the image with my finger, it vanishes off screen. Any ideas what’s going on?

Wolfgang

Great intro! Thanks.
I used jquery-ui and touchpunch, but there where unexpected results. The moved element flickered an jumped to other positions. This Tipp was the solution – Thanks!

Picawho

Good job but how do I add an array of object and properties such as move don’t move?
I want an option to creat up to 40+ objects and drag them or auto space them accordinglyPicawho@hotmail.com thanks.

Picawho@hotmail.com

How do I create an array of div with different colors and all dragable? My project needs an option for items. User selects maybe using a slider or list select from 0-100. Only the selected items which are divs should be displayed on the screen. However each div should be dragable and double clickable. Using the example on Sewa.se/drag