Forums

The forums ran from 2008-2020 and are now closed and viewable here as an archive.

Home Forums JavaScript Touch device ghost click triggers on different element

  • This topic is empty.
Viewing 11 posts - 16 through 26 (of 26 total)
  • Author
    Posts
  • #252922
    Shikkediel
    Participant

    Looking at the answers on SO, I believe most of these script work because of using preventDefault() on touchstart. The only touch event for which it will have a cross browser reliably effect that will prevent it getting emulated into a click. Going with touchstart and click is the shortest route of course, although all native behaviour (scroll, swipe) will be disabled when starting the interaction on the element in question.

    It’s going a bit out of the scope of the original issue but the custom event I put together has a shortcoming as well that is in the same sphere. Which is that if you happen to touch an element with the event listener and start scrolling, after removing the finger the event might get triggered. I may have to add a solution for that (likely awkward and overly complicated with Android again) on the page that had the issues.

    Some good info for future reference:

    quirksmode.org/mobile
    github.io/touch/tests

    #252927
    Shikkediel
    Participant

    Never mind this one, I’ll check the code again first.
    For now I can’t see straight anymore…

    codepen.io/anon/pen/aJEKRY

    #252939
    Shikkediel
    Participant

    For now I can’t see straight anymore…

    Still enough to not stop, I suppose. :-/

    But this one’s rather nice…

    $('div').on('touchstart click', {flag: false}, function(e) {
    
    if (e.type == 'click' && e.data.flag) return;
      e.data.flag = e.type == 'touchstart';
    
    //do stuff on either event
    });
    

    codepen.io/anon/pen/wJpYBO

    Even though it doesn’t reset, which would be convenient with devices that support both.

    Now I’m calling it a day…

    codepen.io/anon/pen/evyPvB

    $('div').on('touchstart touchcancel click', {flag: false}, function(e) {
    
      if (e.type == 'touchcancel' || e.type == 'click' && e.data.flag) {
        e.data.flag = false;
        return;
      }
    
      e.data.flag = e.type == 'touchstart';
    
      //do stuff on either event
    });
    
    #252944
    Shikkediel
    Participant

    I’ve heard that this solution from Stackoverflow takes care of the ghost click:

    $(document).on('touchstart click', '.myBtn', function(event){
        if(event.handled === false) return
        event.stopPropagation();
        event.preventDefault();
        event.handled = true;
        // Do your magic here
    });
    

    Looks like that was extracted from some jsbin code:

    codepen.io/anon/pen/yMvLmx

    That doesn’t seem to work either, at least not with Firefox touch emulation. Even though using a flag at all doesn’t seem necessary when preventDefault is also in place…

    var kind;
    
    function TouchClick(sel, fnc) {
      $(sel).on('touchstart click', function(event) {
        event.stopPropagation();
        event.preventDefault();
        $('div span').append(event.handled + '<br>');
        if (event.handled !== true) {
          kind = event.type;
          fnc(event);
          event.handled = true;
        } else {
          return false;
        }
      });
    }
    
    TouchClick('div', function() {
      $('div span').append(kind + '<br>');
    });
    

    Here event.handled is just as undefined on each occurence as with the smaller script.

    #252949
    Mottie
    Member

    Hehe thanks for continuing to work on this… did you try adding the css property?

    #252963
    Shikkediel
    Participant

    That’s a new concept for me so I’m still reading up on it. :-)
    Googling a bit and landing on jQuery event data was a nice find too…

    I’m usually quite relentless when trying to get to the bottom of some code.
    Often up to a point where I tire myself (and maybe even others) majorly. :-/

    #252964
    Shikkediel
    Participant

    Feel free to in return incorporate any of my fiddling-findings if you think it might enhance your own SO answer by the way, Mottie… the format there is of a wiki community after all. With 32 existing answers, I definitely won’t be adding one myself.

    #252966
    Mottie
    Member

    Well actually that question/answer on SO is about binding to touch & click events, not really about ghost clicks. I think you should find a question and add an answer there since you’re doing all this work – I’d up-vote you!… or at least write up a blog post somewhere and share a link here.

    #252972
    Shikkediel
    Participant

    I think the code at the top is what the other answer was intended to be but with all the details discussed in the comments, yours does look quite complete.

    There was an underwhelming topic on SO where someone asked how to switch between touch and mouse. It already having an accepted answer, I suggested to listen to both by posting some rather crude and flawed code. Quite a while later someone found and upvoted this so I felt obliged to at least post a bug free version. That’s enough for me.

    It’ll be a while before I turn the last stone on this and come up with the most ideal code…

    #253037
    Shikkediel
    Participant

    Original “ghost click” issue – an old Android bug, sort of solved…

    But here’s the final code I’m using to create a page:tap, eliminating the more commonly known ghost click that comes from touch events being emulated:

    function swiftClick(aim) {
    
      $(document).on('mousedown touchstart click', aim, {}, function(e) {
    
        var mean = $(e.currentTarget), info = e.data;
    
        if (e.type == 'mousedown' && (e.which != 1 || info.hit)) return;
        if (e.type == 'click') {
          delete info.hit;
          return false;
        }
    
        info.peg = Date.now();
    
        mean.one('mouseup touchend', function(e) {
    
          if (e.type == 'touchend') info.hit = true;
          else if (info.hit) return;
          if (Date.now()-info.peg > 300) return;
          mean.trigger('page:tap');
        });
      }).on('page:tap', aim, function() {
    
        if ($(this).is('a')) window.location = this.href;
      });
    }
    

    Usage:

    swiftClick('.something');
    
    $('.something').on('page:tap', function() { // do whatever });
    

    Using persistent event data’s a bit nicer than adding classes. Clicks on links will have their default behaviour blocked and get redirected by the script. Subsequent touchstart-touchend or mousedown-mouseup will not trigger a page:tap if it took longer than 300ms.

    It’s going a bit out of the scope of the original issue but the custom event I put together has a shortcoming as well that is in the same sphere. Which is that if you happen to touch an element with the event listener and start scrolling, after removing the finger the event might get triggered.

    In fact, default touchcancel behaviour on many browsers will make the touchend not fire in that case which is quite convenient. But the 300ms time limit should also mostly solve it.

    #253039
    Shikkediel
    Participant

    Okay, small edits don’t seem to be allowed… let me post again.

    Original “ghost click” issue – an old Android bug, sort of solved…

    But here’s the final code I’m using to create a page:tap, eliminating the more commonly known ghost click that comes from touch events being emulated:

    function swiftClick(aim) {
    
      $(document).on('mousedown touchstart click', aim, {}, function(e) {
    
        var mean = $(e.currentTarget), info = e.data;
    
        if (e.type == 'mousedown' && (e.which != 1 || info.hit)) return;
        if (e.type == 'click') {
          delete info.hit;
          return false;
        }
    
        info.peg = Date.now();
    
        mean.one('mouseup touchend', function(e) {
    
          if (e.type == 'touchend') info.hit = true;
          else if (info.hit) return;
          if (Date.now()-info.peg > 300) return;
          mean.trigger('page:tap');
        });
      })
      .on('page:tap', aim, function() {
    
        if ($(this).is('a')) location = this.href;
      });
    }
    

    Usage:

    swiftClick('.something');
    
    $('.something').on('page:tap', function() { // do whatever });
    

    Using persistent event data’s a bit nicer than adding classes. Clicks on links will have their default behaviour blocked and get redirected by the script. Subsequent touchstart-touchend or mousedown-mouseup will not trigger a page:tap if it took longer than 300ms.

    It’s going a bit out of the scope of the original issue but the custom event I put together has a shortcoming as well that is in the same sphere. Which is that if you happen to touch an element with the event listener and start scrolling, after removing the finger the event might get triggered.

    In fact, default touchcancel behaviour on many browsers will make the touchend not fire in that case which is quite convenient. But the 300ms time limit should also mostly solve it.

Viewing 11 posts - 16 through 26 (of 26 total)
  • The forum ‘JavaScript’ is closed to new topics and replies.