1
votes

I have an angular.js webapp which use shortcuts. When I press any key, I get N keydown events, then N keyup events, where N is between 2 and 5, while I expect it to fire only once. Here is the code that I use to catch keyboard events:

// Controller.js
var allowed = true;
$document.bind('keyup', function (event) {
  console.log('keyup');
  allowed = true;
});
$document.bind('keydown', function (event) {
  if (allowed) {
    allowed = false;
    var key = event.which || event.keyCode || event.charCode;
    console.log('key down: ' + key);
    if (key === 32) { // Space bar
      $scope.showAll();
      event.stopPropagation();
    }
    else if ($scope.visible.answerButtons === true) {
      if (key === 37) { // Left arrow, wrong answer
        $scope.session.addAnswer(false);
        event.stopPropagation();
      }
      else if (key === 39) { // Right arrow, right answer
        $scope.session.addAnswer(true);
        event.stopPropagation();
      }
    }

    if (!$scope.$$phase) { $scope.$apply(); }
  }
});

The use of the allow variable does not work here since all keydown events are fired before keyup. The result when I press any key is:

"key down: 32" 
"keyup" 
"key down: 39" // I'm pressing the right arrow only once, and release it immediately
"key down: 39" // but it fires 3 times
"key down: 39"
"keyup"
"keyup"
"keyup"

Note that everything works fine the first time I display the page. The problem occurs when I display it again (reload the template and the controller, not refreshing the entire app using F5).

Any idea about why I get all these events after the first display ?

EDIT:

Okay, I got it, the reason why it fires multiple events after the first display is simply because angular does not remove event listeners on scope destruction. Therefore, old listeners are kept, so when I reload the page, I get several handlers for the same event, which do the same thing. So when I display the page N times, there is N handlers for keydown event.

The solution is to remove manually the handlers on $destroy event (keyboardManager can be found at the link Dave gave in his answer, you can use $document.unbind as well):

keyboardManager.bind('space', function () {
  console.log('press space');
});
keyboardManager.bind('right', function () {
  console.log('press right');;
});
keyboardManager.bind('left', function () {
  console.log('press left');
});

$scope.$on('$destroy', function () {
  keyboardManager.unbind('space');
  keyboardManager.unbind('right');
  keyboardManager.unbind('left');
});
1
did u tried event.preventDefault(); inside keydown event.Dave
I just tried it, but it doesn't solve the problemCarl Levasseur

1 Answers

4
votes

Did you try event.stopImmediatePropagation() ? It solves this I think.