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?