11

I'm using hammer.js and it appears that I event.stopPropagation() doesn't work with tap event.

If I click on the child, the associated event is triggered but parent's event is also triggered and I don't want that.

$('#parent').hammer().bind('tap', function(e) {
    $(this).css('background', 'red');
});​​​​​​​​

$('#child').hammer().bind('tap', function(e) {
    e.stopPropagation();
    $(this).css('background', 'blue');
});

Here is an example: http://jsfiddle.net/Mt9gV/

​ I also tried with jGestures and the issue seems to be the same. How can I achieve this result with one of those library? (or another one if it is needed)

j08691
  • 204,283
  • 31
  • 260
  • 272
TimPetricola
  • 1,491
  • 2
  • 12
  • 24
  • I think it may have something to do with the fact that you've set up the "hammer" stuff on both the parent and the child elements. I'm not 100% sure however. – Pointy Jun 13 '12 at 17:13
  • Unfortunately if I don't apply hammer to both elements, the tap event will not work. – TimPetricola Jun 13 '12 at 17:19

5 Answers5

9

As mentioned in another comment, one would expect, as you did, that calling event.stopPropagation() would be all that is require to stop the event from bubbling up to the parent.

Details: However, in Hammer.js this is not so. Hammer creates a new gesture state machine for each element on which you call $(el).hammer(). So when a touch event is fired on the child by the browser it is processed first by the logic for child and then again by the logic on the parent. As such to stop the parent from getting the tap event you must prevent the parent's hammer state machine from getting the original touch event. Generally, calling event.stopPropagation() will also stop the propagation of the original event. However, hammer.js's tap event is fired on touchend but the originalEvent is the cached touchstart event. As such, stopping propagation on the original event has no effect because the touchstart event has long ago bubbled up through the DOM.

Solution? I believe this is a bug in hammer - submit a pull request that corrects the originalEvent in the tap event. As others have suggested you could check the event's target, but that's always the parent element - another bug in my opinion. So instead check the event.originalEvent.target. Here's a JS fiddle with this mediocre solution implemented: http://jsfiddle.net/HHRHR/

rharper
  • 2,438
  • 20
  • 14
  • 6
    Note: My answer to this question is out of date - hammer.js has been changed significantly since this was posted. – rharper Jun 19 '13 at 17:55
  • We use code nearly identical to TimPetricola's original snippet. I can confirm it works as expected with Hammer.JS - v1.0.5 - 2013-04-07. Looks like Hammer.js took this issue seriously and fixed it. – Douwe Jul 19 '13 at 10:28
4

I had a similar problems with the touch events.

I solved it by using

return false;

which is the same as calling e.preventDefault(); e.stopPropagation();

Leo
  • 1,495
  • 23
  • 41
3

As I couldn't get this work, I used a variable to know if the child event has already been triggered. It works quite well with jGestures (I have not tried with hammer.js)

var childTriggered = false;

$('#child').bind('tapone', function(e) {
    $(this).css('background', 'red');
    childTriggered = true;
});

$('#parent').bind('tapone', function(e) {
    if(childTriggered) {
        childTriggered = false;
        return;                
    }
    $(this).css('background', 'blue');
});

TimPetricola
  • 1,491
  • 2
  • 12
  • 24
0

If you can't get this to work.. just call another method on the tap after verifying the needful (arguments.callee is where I would start in such a case) , this would have the added benefits of letting other users hook the button also...

function myFn(a) { if(a) $(this).css('background', 'red'); else $(this).css('background', 'blue');}

$('#parent').hammer().bind('tap', function(e) {
   //Check arguments.callee or other data in event and call myFn
});​​​​​​​​

$('#children').hammer().bind('tap', function(e) {
  //Same here
});
Jay
  • 3,276
  • 1
  • 28
  • 38
  • 2
    `arguments.callee` is [now deprecated](https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope/arguments/callee) – chrisfrancis27 Jun 13 '12 at 17:15
  • I would imagine that is only in Strict mode... https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope/arguments/callee and I would not let such a small thing stop me from finishing the task at hand ... use this and go back to the problem once you are finished. – Jay Jun 13 '12 at 17:19
  • Because it gives no performance improvement... and who cares? http://stackoverflow.com/questions/3145966/is-strict-mode-more-performant – Jay Jun 13 '12 at 17:23
  • strict is for beginners IMHO to not screw things up. – Jay Jun 13 '12 at 17:24
  • Well go figure it out yourself... obviously its something u or your library is doing... I dont have this problem in my appz. – Jay Jun 13 '12 at 17:30
0

You could try checking e.currentTarget (or is it e.target?) inside the parent handler to find out which element was tapped; if it's the child element then just return immediately, else continue with the function.

chrisfrancis27
  • 4,516
  • 1
  • 24
  • 32
  • Nice idea but it return the parent element even if I click on the child. – TimPetricola Jun 13 '12 at 18:18
  • Hmmm. I have a suspicion that Hammer actually binds to the `window` object to do all its calculations, working out the global X/Y coordinates of a touch event and then determining which element was clicked from that. In which case, stopPropagation won't have any effect. But I'm just guessing from a quick glance at the [Hammer.js source](https://github.com/EightMedia/hammer.js/blob/master/hammer.js)! – chrisfrancis27 Jun 13 '12 at 18:30