Prevent swipe from triggering ng-click handler
Asked Answered
H

2

13

I'm working on implementing an iOS-like swipe-to-delete gesture on HTML table rows. For example, a leftwards swipe on Site11 will turn it from a standard row:

A standard HTML table

into a delete-able row:

A standard HTML table with a delete-able HTML row

I have this functionality working with the ng-swipe-left directive. However, I also have a ng-click directive on each row that navigates to a different view of the application. Currently, both events are triggered when I perform a swipe on a row, except when the swipe ends on the "Site11" text itself, as opposed to anywhere else within the row. For example, this gesture will trigger both the ng-click and the ng-swipe-left handlers:

enter image description here

but this gesture will only trigger the ng-swipe-left handler:

enter image description here

How can I prevent the ng-click handler from being fired if a swipe is performed on the row, regardless of where the swipe ends?

Here's the gist of my HTML structure that defines each row:

<tr ng-repeat="item in items">
  <td ng-click="openDetailPane()"
      ng-swipe-left="$parent.swipeDeleteItemId = item.Id" 
      ng-swipe-right="$parent.swipeDeleteItemId = 'none'">
    <div list-item></div>
  </td>
  <td>
    <i class="fa fa-angle-right fa-2x" />
      <span>{{item.ChildCount}}</span>
  </td>
</tr>

The delete button is defined inside the list-item directive; it is only visible if its ID matches the swipeDeleteItemId property on the controller:

<div class="list-item">
  <span>{{item.Name}}</span>
  <div ng-class="{true: 'is-visible', false: ''}[item.Id === swipeDeleteItemId]">
    <div class="delete-item-swipe-button" 
         ng-mousedown="$event.stopPropagation();" 
         ng-click="$event.stopPropagation();">Delete</div>
  </div>
</div>

I should mention that I've only tried this in the desktop versions of Chrome and IE11 - I'm assuming a click and drag from a mouse registers identically to a swipe on a mobile device.

Hassanhassell answered 8/2, 2014 at 23:9 Comment(0)
D
15

I also met this situation and I finally found a tricky way to do that.
The $event.stopPropagation() mentioned somewhere only works in ngClick. Even writing a custom swipe directive by $swipe with event.stopPropagation() cannot prevent ngClick... So...

$swipe service will trigger both 'touch' and 'mouse' events by default. So does ngSwipeLeft and ngSwipeRight directives.
So when you do the swipe, it will trigger events by the following order:

  1. touchStart
  2. touchMove
  3. touchEnd
  4. mouseDown
  5. mouseUp
  6. click

I tested by mouse drag not touch directly, but my app will run on a touch screen on PC, and the swipe on this touch screen is emulating mouse drag. So the event type of $swipe service 'end' event in my app is 'mouseup'.
Then you can use a flag to do something like this:

<div ng-swipe-left="swipeFunc(); swiping=true;" ng-click="swiping ? ( swiping = false ) : clickFunc();">
   ...
</div>

or

<div ng-swipe-left="swipeFunc(); swiping=true;" ng-mouseup="clickFunc();" ng-click="swiping=false;">
  ...
</div>    

with clickFunc() like following:

$scope.clickFunc = function() {
   if( $scope.swiping ) { return; }
   // do something
}

This works for me. I hope this is also useful for you.

Doubleton answered 31/10, 2014 at 15:21 Comment(1)
Couldn't find the order of firing documented anywhere, thanks for your research, enjoy the bounty.Tassel
I
0

I'm having this same problem right now as well, and indeed only on a desktop browser. I thought that preventDefault() or stopImmediatePropagation() would do the trick but no. However, I did find a solution for it though. Try this:

angular.module('app', [])
.directive('noSwipeClick', function () {
    return function(scope, elm) {
        var el = angular.element(elm);
        el.bind('click', function(e) {
          if(scope.swipe.swiping === true) {
            e.stopPropagation();
            e.preventDefault();
          }
        });
    };
});

And in your HTML:

<div class="list-item">
  <span>{{item.Name}}</span>
  <div ng-class="{true: 'is-visible', false: ''}[item.Id === swipeDeleteItemId]">
    <div no-swipe-click class="delete-item-swipe-button" 
     ng-mousedown="$event.stopPropagation();" 
     ng-click="$event.stopPropagation();">Delete</div>
  </div>
</div>

Don't forget to assign $scope.swipe.swiping = true in your controller when actually swiping and setting it to false when done

Inurn answered 4/7, 2014 at 10:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.