It turns out to be possible to handle the touchmove and touchend events with data obtained from the touchstart event object. It is not necessary to access the touchmove and touchend event objects, as long as you continue to have access to the touchstart one.
Apparently, the touchstart event object persists in browser memory even when the event has long ended. More importantly, it continues to be updated with information about the current touch action.
This is interesting. It’s also profoundly different from the desktop, where a similar trick with the mousedown, mousemove, and mouseup events definitely does not work.
Both iPhone and Android display this behaviour. Therefore future implementations of the
touch events should, too.
Update: I’ve been given to understand that this behaviour will disappear from WebKit. So don’t build your scripts with this behaviour; they’ll misfire sooner or later.
See Touching and Gesturing on the iPhone for more information on how the touch events work on the iPhone.
While tinkering with my scrolling layer test page I found out that I’d made an error in the first version. Oddly, this first version worked absolutely fine on both iPhone and Android; that’s why I didn’t notice the error at first.
I just plain forgot to give the e
argument to the touchend function.
The script got its touchend information from the touchstart event object:
var testEl = [the scrolling layer]; testEl.ontouchstart = function (e) { // do stuff var origin = getCoors(e); testEl.ontouchmove = moveDrag; testEl.ontouchend = function () { <-- forgot the e var end = getCoors(e); <-- e is touchstart object // do stuff testEl.ontouchmove = testEl.ontouchend = null; } function moveDrag(e) { var currentPos = getCoors(e); // move layer } function getCoors(e) { var coors; if (e.touches && e.touches.length) { // iPhone coors = e.touches[0].clientX; } else { // all others coors = e.clientX; } return coors; } }
I found the error because I wanted to make the script desktop-compatible in order to prove that from an interaction point of view it makes no sense to just swap the mouse events in place for the touch events.
I added mousedown, mousemove, and mouseup event handlers, saw an abrupt stop in Firefox
instead of the smooth stop, and discovered the missing e
. Incidentally, this
proves that the mouse events do
not have persistent event objects (something we’ve known for 10 years, but still).
My curiosity played up. Apparently, handling the touchend event with information obtained from the touchstart event object works fine. Could I rewrite the script so that it only uses the touchstart event object, and entirely ignores the touchmove and touchend objects?
The answer turns out to be Yes. The following script works fine on iPhone and Android:
var testEl = [the scrolling layer]; testEl.ontouchstart = function (e) { // do stuff var origin = getCoors(); testEl.ontouchmove = moveDrag; testEl.ontouchend = function () { var end = getCoors(); // do stuff testEl.ontouchmove = testEl.ontouchend = null; setTimeout(checkOneLastTime,1000); } function moveDrag() { var currentPos = getCoors(); // move layer } function getCoors() { // e refers to the touchstart event object // even if the getCoors function is called // ontouchmove or ontouchend. Works fine var coors; if (e.touches) { // iPhone coors = e.touches[0].clientX; } else { // all others coors = e.clientX; } return coors; } function checkOneLastTime() { alert(getCoors()) // works! event object still there } }
In other words, the original touchstart event object persists even when the touchstart event itself has long ended. Even better, it continues to be updated with new information, such as the current touch coordinates.
I also read out the touchstart event coordinates one second after the touchend event fired, i.e. when the entire touch action had totally and irretrievably gone. The event object was still there, although its coordinates weren’t updated any more and it ignored new touch actions.
I also tested whether the same is possible with the touchmove event. The answer is Yes: a similar trick that uses a touchmove event object to retrieve information about the touchend event works.
One could think that there is in fact only one single event object that all touchstart, touchmove, and touchend events use. This turns out not to be the case (though it’s an interesting idea).
When I added the missing e
to the first version of the script it stopped
working on the iPhone. The script now used the touchend event object, which did not contain
the touches
list because there were no longer any touch actions going on. I had
to move to the changedTouches
list to get the coordinates I need.
This proves that all events still cause unique objects to be created: the touchstart
one contains a touches
list; the touchend one doesn’t.
Concluding, you can easily handle the touchmove and touchend events by using the information contained in the touchstart object. There is no need to access the touchmove and touchend event objects.
I wonder if this has performance implications. It probably won’t; the touchmove and touchend event objects must be constructed anyway, since the browser has no way of knowing we won’t use them. (Unless you use only one single event object for an entire touch action sequence.)
This is the blog of Peter-Paul Koch, web developer, consultant, and trainer.
You can also follow
him on Twitter or Mastodon.
Atom
RSS
If you like this blog, why not donate a little bit of money to help me pay my bills?
Categories:
Comments are closed.
1 Posted by Mr Speaker on 3 February 2010 | Permalink
Woah, awesome discovery! When you think about it though, it's just holding the event object via a closure - so it makes sense that it would be updated!
2 Posted by Damien on 4 February 2010 | Permalink
I notice something like that when debugging a swipe event I wrote; I thought at the time that the touch objects in e.touches were shared between the start and move events of a same gesture.
3 Posted by rchl on 9 February 2010 | Permalink
quote: "The script now used the touchend event object, which did not contain the touches list because there were no longer any touch actions going on."
Actually touchend never has touches and targetTouches lists. It only has changedTouches.