While there is a stopPropagation() option to prevent events from bubbling up, for some bizarre reason there is nothing you can do to stop the event (or in this case the default prevention) from being applied to all the selected element’s children. And while there is removeEventListener as a counter to addEventListener, there is no opposite “restoreDefault()” to go with preventDefault(). So what the heck do you do when you need to prevent default behavior in one element, but WANT the default behavior in one of its children?

A popular alternative is to not actually prevent default behavior but to “return false;” in the body tag instead. The theory is you can subsequently “return true;” elsewhere to counter it, but if you don’t stop it bubbling up to the BODY tag the “return false;” will trigger too.

Then I stumbled across this in one of my many, many visits to stackoverflow.com. I think it’s a rather elegant solution to selectively apply and remove default prevention:

1
2
3
4
5
6
7
8
9
document.ontouchstart = function (event) {
if (!event.elementIsEnabled) {event.preventDefault();}
};
document.ontouchmove = function (event) {
if (!event.elementIsEnabled) {event.preventDefault();}
};
document.ontouchend = function (event) {
if (!event.elementIsEnabled) {event.preventDefault();}
};

Then, in any specific element for which you want to RESTORE default behavior:

event.elementIsEnabled = true;

You could either write a simple function that restores full default behavior to the element, or you can write a more complex function that lets you selectively restore default behavior to one or two touch events. This works best if you give those elements an ID and then on page load initialization selectively restore behavior to those elements. If you do it on a “on click” basis you actually need a double click because the first click would restore the default behavior, the second click would activate the default behavior. On a touch device this “double clicking” might not be that intuitive, but users probably would eventually figure it out (it’s not as if double clicking is a strange and unknown sort of selection action).

Here’s the simple version if you just want to drop it in and use it. It creates two custom functions, preventDefault(); and restoreDefault(); with my modifcation that can be selectively applied to any element (notice that it is preventDefault(element); and not element.preventDefault();). If you leave out the “element” it will apply to the entire document. It doesn’t appear to bubble up to the document level otherwise, at least it didn’t in my tests with mobile Safari.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function preventDefault(element) {
if (element==null) { element = document };
element.ontouchstart = function (event) {
if (!event.elementIsEnabled) {event.preventDefault();}
};
element.ontouchmove = function (event) {
if (!event.elementIsEnabled) {event.preventDefault();}
};
element.ontouchend = function (event) {
if (!event.elementIsEnabled) {event.preventDefault();}
};
}

function restoreDefault(element) {
if (element==null) { element = document };
element.ontouchstart = function (event) {
if (!event.elementIsEnabled) {event.elementIsEnabled = true;}
};
element.ontouchmove = function (event) {
if (!event.elementIsEnabled) {event.elementIsEnabled = true;}
};
element.ontouchend = function (event) {
if (!event.elementIsEnabled) {event.elementIsEnabled = true;}
};
}

In my BODY tag I added:

1
onload="init();" ontouchstart="defaultPrevention();"

This triggers a custom function:

1
2
3
4
5
6
7
function defaultPrevention() {
if (navigator.userAgent.match(/Android/i)){
window.scrollTo(0,1);
preventDefault();
}
else {preventDefault();}
}

The first part gives Android users more of an “app view” by scrolling the URL bar out of the way and then locking the page size (you can’t scroll back up and get the URL bar, but you can use the back button or close the page to go somewhere else). The second part is for default prevention on iOS devices.

On iOS devices if a user bookmarks a page to their home screen, and the page contains meta tag “apple-mobile-web-app-capable” it can be used in a web view without the URL bar or bottom toolbar (making it behave like a native app). Android doesn’t have this same functionality and will always show a URL bar. But the defaultPrevention() function above will move it out of the way and keep the page maximized, which gives the Android user a similar experience to the iOS “web app capable” feature.

If you don’t want to hide the URL bar for Android users, though, you don’t need this function at all. Just call “preventDefault()” either ontouchstart in the body tag or just add it to your init() on page load. This creates a consistent experience for both Android and iOS when using the site through the browser because both will show a URL bar at the top of the window.

However, in my particular application I was trying to replicate touch-scrolling a list and tapping to select. The aforementioned method works, but only with the double click and then the default prevention has to be reapplied to the list item or it will immediately fire as a selection the next go round.