I use Hammer.js to detect touches on mobile devices, tap, swipe etc.
I have an interaction where on tap, i hide the tapped content (and possibly parents) and show some other content in its place (change screen - like functionality).
The problem is that the newly visible contents may have events of their own binded on them, or may natively interact on tap (eg labels toggling checkboxes, text inputs being focused). If the components are hidden/displayed as soon as tap occurs, the 400ms click event is still running and is then triggered on the elements below.
Check out this jsfiddle on mobile:
http://jsfiddle.net/annam/xGJZL/
http://jsfiddle.net/annam/xGJZL/embedded/result/
<div class="checkbox">
<input type="checkbox" id="check" />
<label for="check"></label>
</div>
<div class="box"></div>
<style>
.checkbox { width: 500px; height: 500px; position: absolute; top: 0; ; left: 0; }
.checkbox input { display: none; }
.checkbox label { display: block; width: 100%; height: 100%; background: yellow; }
.checkbox input:checked + label { background: green; }
.box { position: absolute; top: 0; left: 0; width: 200px; height: 200px; background: pink; }
</style>
<script>
$('.box').hammer().on('tap', function(e){ $(this).hide(); })
$('label').hammer().on('tap', function(e){ $('.box').show(); })
</script>
Check out how on tap, the label below is toggled (MOBILE ONLY!). This doesn't happen on the web, it happens because a native click event is triggered on the label because it's visible on the tap position within 400ms of the touchstart event.
Also try to change the tap event on the label to a native click event. This is also incorrectly triggered (MOBILE ONLY!).
Other instances I've noticed this happen other than click events and labels is input fields, where the text input field is focused (and keyboard pops up) as soon as it is displayed behind the tap event.
Using preventDefault and stopPropagation does not fix this issue as it's not an issue with event bubbling, and the event that is prevented is actually "tap", where as we need to stop is something in the range of click/mousedown/touch. This seems to happen with both hammerjs v1 and v2 (v1 with e.gesture.preventDefault()
etc doesn't work either).
Any way to avoid this?
After some feedback from the creator of Hammer.js, Jorik, here's two distinct solutions:
Solution 1
clickbuster.js, which preventsDefault and stopsPropagation on clicks events occurring at the same position within a given timeframe after the touchend event. This seems to work great for preventing binded click events and for preventing labels from toggling checkboxes. doesn't work for preventing inputs from being focused though. Works for both Hammer v1 and v2. just embed the script and it handles all the rest.
Solution 2
e.gesture.srcEvent.preventDefault()
which preventsDefault on the native touchstart event. This seems to be more foolproof and also works for the input focus issue. only works with hammer v2 though. Needs to be called inside all .on('tap') binds