Skip to content

Instantly share code, notes, and snippets.

@BobNisco
Last active February 27, 2023 15:43
Show Gist options
  • Save BobNisco/9885852 to your computer and use it in GitHub Desktop.
Save BobNisco/9885852 to your computer and use it in GitHub Desktop.
onLongPress AngularJS Directive - Great for mobile!
// Somewhere in your controllers for this given example
// Example functions
$scope.itemOnLongPress = function(id) {
console.log('Long press');
}
$scope.itemOnTouchEnd = function(id) {
console.log('Touch end');
}
// Add this directive where you keep your directives
.directive('onLongPress', function($timeout) {
return {
restrict: 'A',
link: function($scope, $elm, $attrs) {
$elm.bind('touchstart', function(evt) {
// Locally scoped variable that will keep track of the long press
$scope.longPress = true;
// We'll set a timeout for 600 ms for a long press
$timeout(function() {
if ($scope.longPress) {
// If the touchend event hasn't fired,
// apply the function given in on the element's on-long-press attribute
$scope.$apply(function() {
$scope.$eval($attrs.onLongPress)
});
}
}, 600);
});
$elm.bind('touchend', function(evt) {
// Prevent the onLongPress event from firing
$scope.longPress = false;
// If there is an on-touch-end function attached to this element, apply it
if ($attrs.onTouchEnd) {
$scope.$apply(function() {
$scope.$eval($attrs.onTouchEnd)
});
}
});
}
};
})
<ion-item ng-repeat="item in list" on-long-press="itemOnLongPress(item.id)" on-touch-end="itemOnTouchEnd(item.id)">
{{ item }}
</ion-item>
@negesti
Copy link

negesti commented Jun 2, 2016

After trying to get this to work on mobile safari without "killing" scrolling for 2 hours, i have to thank you and post my solution.

css class
create a css class, that will prevent text selection on mobile devices.
NOTE if you have the onLongPress on a div, that contains input elements, the text inside the input will be selected even with this css

.unselectable {
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
}  

The directive

      function ngLongTouch($timeout, $parse) {
        return {
          restrict: 'A',
          link: function(scope, elem, attrs) {
            var timeoutHandler;
            var fn = $parse(attrs.ngLongTouch);

            // disable text selection
            elem.unselectable = "on";
            elem.addClass("unselectable");

            elem.bind('touchstart', function(event) {
              scope.longTouchTriggered = false;
              timeoutHandler = $timeout(function() {
                scope.longTouchTriggered = true;
                fn(scope, { $event:event });

              }, 600);
            });

            elem.bind('touchend', function() {
              if (scope.longTouchTriggered) {
                event.preventDefault();  
              }
              $timeout.cancel(timeoutHandler);
            });
          }
        }
      };
  }
};

To prevent normal ng-click events from firing, i added scope.longPressTriggered that is checked in touchend and calls preventDefault(). I needed this for two cases

  • a normal ng-click event on my div was called after the ngLongTouch
  • on long touch i open a modal and the buttons inside the modal are "pressed" on touchend

@fatemab
Copy link

fatemab commented Jun 28, 2016

Great directive but it does not work on window's phone. can you help with this?

@fatemab
Copy link

fatemab commented Jun 28, 2016

I got solution for window's phone.. You have to use "pointerdown" and "pointerup" events for windows.
posting code if any one needs:
app.directive('onLongPress', function ($timeout) {
return {
restrict: 'A',
link: function ($scope, $elm, $attrs) {

        var onlongtouch;
        var timer;
        var touchduration = 500;

        onlongtouch = function () {
            $scope.$apply(function () {
                $scope.$eval($attrs.onLongPress);
            });
        };

        touchstart = function () {
            timer = setTimeout(onlongtouch, touchduration);
        };

        touchend = function () {
            //stops short touches from firing the event
            if (timer)
                clearTimeout(timer); // clearTimeout;
        };


        ang_element = angular.element($elm);
        raw_element = ang_element[0];
        raw_element.addEventListener('pointerdown', touchstart);
        raw_element.addEventListener('pointerup', touchend);
        raw_element.addEventListener('touchstart', touchstart);
        raw_element.addEventListener('touchend', touchend);
    }
};

});

@persianturtle
Copy link

persianturtle commented Jul 13, 2016

When using this directive in combination with ngClick, I encountered a problem on the iPad. On the iPad, a touch even occurs after the long press directive executed the function with $eval. The solution is to just just preventDefault() to prevent this behavior, but this would stop normal ngClick behavior. To make this directive usable on an element with ngClick, I've added logic to only preventDefault() for long presses.

link: function(scope, element, attributes) {
  var timeoutHandler;

  element.bind('mousedown touchstart', function() {
    scope.start = Date.now();
    timeoutHandler = $timeout(function() {
      scope.$eval(attributes.onLongPress);
    }, 600);
  });

  element.bind('mouseup touchend', function(e) {
    scope.end = Date.now();
    if (scope.end - scope.start >= 600) {
      e.preventDefault();
    }
    $timeout.cancel(timeoutHandler);
  });
}

@marcramser
Copy link

Thanks a lot! Save me much time.

@raviji
Copy link

raviji commented Sep 17, 2016

For me not working on ipad guys, please help me out

@Vespira
Copy link

Vespira commented Sep 19, 2016

Thanks I took this code and adapted it, it works well. I'm just not sure it's a good thing to manually "$eval" methods. I've heard that normally you should always let angular eval automatically the things at each cycle.

@3Nex
Copy link

3Nex commented Sep 26, 2016

I've found that the long press event fires even if you just grab on that element to scroll down the page. So I've added the following touchmove event to cancel the long press from firing, and prevent that behavior:

$elm.bind("touchmove", function(evt) {
    $scope.longPress = false;
});

or for @mbaer3000's solution:

elem.bind("touchmove", function(evt) {
    $timeout.cancel(timeoutHandler);
});

@bharathidhasanS
Copy link

touchmove event not working as expected. Any help

@bhuvan-socialcurry
Copy link

i need to select multiple elements on long press can somebody help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment