Introduction
This article will look at a script I've developed to simplify improving the performance of websites when looked at through a mobile phone.
Background
Recently whilst working on a new project, we all booted up our mobile phones and started testing. Immediately we all noticed that the amount of time required for the click event to fire through a touch on a mobile device was substantial and significantly reduced the usability of the application. Double taps came through as single clicks with the second tap being lost.
The reason this happens is because mobile phones support the touch based event architectures. Ontouchstart
, ontouchend
and ontouchmove
are all new events which can be specified within you mark up and made use of on a mobile. When these events where added to the HTML to event started firing immediately.
The delay is because the phone's browser will look for a touch event all the way up to the window object before firing the click event. Hence the delay.
We investigated a number of ways to work around this.
The obvious way would be to specify both touch and click events. But we didn't want the repetition especially as we didn't need any of the benefits touch events bring such as multi-touch and gestures e.t.c.
We looked at using JS to navigate the DOM on load and reassign all click events to touch events. This approach does work as long as you don't modify the DOM after the reassignment without reassigning again. But I considered this a development risk that would cause problems moving forward.
In the end we came up with the following script, which is cross browser and, which, assuming you simply want a click programmed website to work better on a phone, works like a dream!
Using the code
The following is the code block, please see the comments to understand how it works.
It works on IE8+, Chrome, Safari Win & Mac, Firefox.
function touchDeviceTest() {
try {
document.createEvent("TouchEvent");
return true;
} catch (e) {
return false;
}
}
var tchHasScrolled = false;
var tchRandomId = 0.0;
var tchRandomLastId = 0.0;
var touchYTemp = 0;
function onDocumentScroll(e) {
tchHasScrolled = true;
}
function onDocumentClick(e) {
if (tchRandomId != tchRandomLastId) {
tchRandomLastId = tchRandomId;
if (touchDeviceTest()) {
if (e.target.tagName != 'A') {
e.stopPropagation();
e.preventDefault();
}
}
}
}
function onDocumentTouchStart(e) {
tchHasScrolled = false;
tchRandomId = new Date().getMilliseconds() + Math.random();
touchYTemp = e.touches[0].clientY;
}
function onDocumentTouchMove(e) {
var y = e.touches[0].clientY;
if (y > touchYTemp + 10 || y < touchYTemp - 10) {
tchHasScrolled = true;
}
}
function onDocumentTouchEnd(e) {
if (e.touches[0]) {
var y = e.touches[0].clientY;
if (y > touchYTemp + 10 || y < touchYTemp - 10) {
tchHasScrolled = true;
}
}
if (!tchHasScrolled) {
var target = e.target.parentNode;
if (e.target.onclick) {
e.target.onclick(e);
} else {
while (target != null) {
if (target.onclick) {
target.onclick(e);
break;
}
target = target.parentNode;
}
}
}
}
if (!document.addEventListener) {
document.attachEvent('onclick', onDocumentClick);
document.attachEvent('onscroll', onDocumentScroll);
document.attachEvent('ontouchstart', onDocumentTouchStart);
document.attachEvent('ontouchmove', onDocumentTouchMove);
document.attachEvent('ontouchend', onDocumentTouchEnd);
} else {
document.addEventListener('click', onDocumentClick, true);
document.addEventListener('scroll', onDocumentScroll, true);
document.addEventListener('touchstart', onDocumentTouchStart, true);
document.addEventListener('touchmove', onDocumentTouchMove, true);
document.addEventListener('touchend', onDocumentTouchEnd, true);
}
Essentially we're using the touch events on the window and the event target within the args to intercept the touch event and where found, execute the click event.
Points of Interest
Whilst the execution of the the link within an Anchor tag is not defined as an onclick event. Calling e.preventDefault
within the event propagation for the click event will stop the anchor from working. This is why I specifically added an exclusion for Anchors.