Hammer.js click triggered on DOM elements behind tapped after tap action
Asked Answered
D

1

7

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?

Dorotea answered 28/7, 2014 at 15:3 Comment(0)
D
8

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

Dorotea answered 31/7, 2014 at 15:38 Comment(3)
The second solution worked perfect. I didn't try the first solution.Always
just embedding PreventGhostClick.js does not handles all the rest, you must define which element to be prevented for ghost clicks as written in comments. PreventGhostClick(myElement); and it is also written in comments that PreventGhostClick(document); (not recommended!!). So your second solution is fine for me.Middling
My 2 cents: remove or strike the first solution. Second is too simple and elegant. Nobody uses v1 anymore.Perni

© 2022 - 2024 — McMap. All rights reserved.