最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - keydown fires multiple times before keyup - Stack Overflow

programmeradmin1浏览0评论

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');
});

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');
});
Share Improve this question edited Apr 30, 2014 at 12:35 Carl Levasseur asked Apr 30, 2014 at 10:50 Carl LevasseurCarl Levasseur 4,7115 gold badges20 silver badges19 bronze badges 2
  • did u tried event.preventDefault(); inside keydown event. – Dave Commented Apr 30, 2014 at 10:53
  • I just tried it, but it doesn't solve the problem – Carl Levasseur Commented Apr 30, 2014 at 11:04
Add a ment  | 

1 Answer 1

Reset to default 5

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

发布评论

评论列表(0)

  1. 暂无评论