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:

document.addEventListener("touchstart", function(e) {
  var touch = 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:

var xPos = e.changedTouches[0].pageX;
var yPos = e.changedTouches[0].pageY;

Check this page to see all available attributes

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:

$(document).bind("touchstart touchmove", function(e) {
  //Disable scrolling by preventing default touch behaviour
  e.preventDefault();
  var orig = e.originalEvent;
  var x = orig.changedTouches[0].pageX;
  var y = 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:

var moveMe = function(e) {
  e.preventDefault();
  var orig = 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:

$.fn.draggable = function() {
  var offset = null;
  var start = function(e) {
    var orig = e.originalEvent;
    var pos = $(this).position();
    offset = {
      x: orig.changedTouches[0].pageX - pos.left,
      y: orig.changedTouches[0].pageY - pos.top
    };
  };
  var moveMe = function(e) {
    e.preventDefault();
    var orig = 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

Read more:

Safari Reference: handling events


  • http://popdevelop.com Johan Brissmyr

    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

  • http://developerquestion.com/how-to-make-this-code-working-on-iphone-and-use-raw-javascript-not-use-jquery-closed/ how to make this code working on iphone , and use raw javascript , not use jquery . [closed] | DeveloperQuestion.com
  • Douglas

    Thanks for the article, I’m going to be trying this out later. The demo seems to be a 404 though.

  • http://sewa.se Sebastian Wallin

    Thanks for pointing it out! It has been fixed now, don’t know what happend…

  • http://questionlounge.com/drag-and-drop-javascript-in-ipad/ drag and drop javascript in Ipad – Question Lounge

    [...] to get this thing done. I could find the way to move the element on the screen.Here the the url http://popdevelop.com/2010/08/touching-the-web/Now what i want to know is, when i drag any element, i need to put it in a predefine area, like drag [...]

  • Steve7642

    The demo works !  a minor miracle

  • Michael Evans33

    How would you get multiple objects on the screen at the same time.

  • http://sewa.se Sebastian Wallin

    Hi!

    You mean like having two objects that could be moved simultaneously using two fingers?

    Easiest is to bind the events to the objects itself instead of to the document as in my example above. Something like this:

    https://gist.github.com/1223979

  • http://sewa.se Sebastian Wallin

    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.

  • http://sewa.se Sebastian Wallin

    Yes, you are correct! If you are looking for a full solution I can recommend using jQuery UI together with this:
    https://github.com/furf/jquery-ui-touch-punch

    works really nice!

  • designq

    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 accordingly
    Picawho@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

     ’Car1 ‘;
     ’Car2 ‘;
     ’Car3 ‘;
     ’CarEtc ‘;

  • ai.syah

    hi! 
    these are my codes.

    //test _1: touchstart, touchmove$(document).bind(“touchstart touchmove”, function(a) {  //Disable scrolling by preventing default touch behaviour  a.preventDefault();  var orig1 = a.originalEvent;  var x = orig1.changedTouches[0].pageX;  var y = orig1.changedTouches[0].pageY;  sym.$(“_1″).css({top: y, left: x});});//test _1: touchend$(document).bind(“touchend”, function(a1) {  //Disable scrolling by preventing default touch behaviour  a1.preventDefault();  var origa1 = a1.originalEvent;  var x = origa1.changedTouches[0].pageX;  var y = origa1.changedTouches[0].pageY;  sym.$(“_1″).css({top: y, left: x});});//test _2: touchstart, touchmove$(document).bind(“touchstart touchmove”, function(b) {  //Disable scrolling by preventing default touch behaviour  b.preventDefault();  var orig2 = b.originalEvent;  var x = orig2.changedTouches[0].pageX;  var y = orig2.changedTouches[0].pageY;  sym.$(“_2″).css({top: y, left: x});});//test _2: touchend$(document).bind(“touchend”, function(b2) {  //Disable scrolling by preventing default touch behaviour  b2.preventDefault();  var origb2 = b2.originalEvent;  var x = origb2.changedTouches[0].pageX;  var y = origb2.changedTouches[0].pageY;  sym.$(“_2″).css({top: y, left: x});});

    –> but the symbols keep moving together diagonally. I wanted to make the symbol to be dragged individually (not dragged together) and dropped it into place (snapped it into place). 

    Thank you.

  • Vallamkapil

    Unable to drag in your final demo

  • yoyonut

    try on simulator or ipad 

  • Javiky

    Where i put a containment there?