在 div 之外单击时如何捕获 ng-keypress/ $event?

How to catch ng-keypress/ $event when clicked outside of div?

我正在 AngularJS 制作数独游戏。我想要实现的是即使我在页面上的 div 之外单击也能捕获 ng-keypress 。看一下 http://sudoku.com/,例如,如果您 select 一个单元格,然后单击页面上您想要的任何位置,然后,如果您单击任何数字,它仍然会更改单元格中的数字.我怎样才能做到这一点?

请查看下面的 HTML 代码:

<table class="sudoku-board" ng-init="getSudoku()" id="sudoku"
                                   ng-class="{'paused': visible}">
                                <tbody>
                                    <tr class="sudoku-row" ng-repeat="sudoku in sudokuGrid track by $index"
                                        ng-init="row = $index">
                                        <td class="sudoku-col sudoku-cell" ng-repeat="number in sudoku track by $index"
                                            ng-init="col = $index" ng-class="{'highlight': colSelected === col || isHighlight(row, col) || rowSelected === row,
                                            'highlight-number':getValue === number.substring(0, 1), 'selected':isSelected === ((row*10) + col), 'paused': visible}"
                                            ng-click="selectedCell(row, col)"
                                            ng-keydown="insertNum($event, row, col);" tabindex="1">
                                            <span class="cell-value"
                                                  ng-class="{'empty': number === null || number.charAt(number.length-1) === '!', 'default': number !== null, 'paused': visible}"
                                                  ng-bind="number.substring(0, 1)"></span>
                                        </td>
                                    </tr>
                                </tbody>
                            </table>

在 ng-keydown 上我触发了这个函数:

// handle inserted value
$scope.insertNum = function (e, row, col, number) {
    console.log("Number: " + number);
    $scope.selectedCol = col; // get selected column
    $scope.selectedRow = row; // get selected row
    console.log(e);
    if (e !== undefined) {
        var keyCode = e.keyCode || e.charCode; // assign key & char code
        if ((keyCode < 49 || ((keyCode > 57 && keyCode < 97) || keyCode > 105)) && (keyCode !== 8 && keyCode !== 46))
            return false; // if clicked button/ event is not a number or delete/ backspace button -> return false

        if (e.currentTarget.children[0].classList[2] === 'empty') // check if clicked cell is empty
            if (keyCode === 8 || keyCode === 46) { // if clicked delete/ backspace remove the current value
                e.currentTarget.children[0].innerHTML = null;
                $scope.sudokuGrid[row][col] = null;
                $(e.target).removeClass("incorrect");
                $(e.target).removeClass("correct");
                $scope.handleErrorClass();
                $scope.getValue = false;
            }
            else { // when number is clicked
                e.currentTarget.children[0].innerHTML = e.key; //  insert that number in the cell
                $scope.sudokuGrid[row][col] = e.key + "!";
                $scope.checkCurrentNumber(row, col);
                $scope.getCurrentNumber(row, col);

                // if inserted number is correct add class
                if (e.key === $scope.sudokuGridSolved[row][col]) {
                    console.log("Correct");
                    $(e.target).removeClass("incorrect");
                    $(e.target).addClass("correct");
                }
                // add incorrect class if inserted number is not correct
                else {
                    console.log("Incorrect");
                    $(e.target).removeClass("correct");
                    $(e.target).addClass("incorrect");
                }
                //$scope.sudokuGrid[row][col] = e.key;
            }
    }
    else {
        if (number !== null && $("tr:eq(" + $scope.selectedRow + ") td:eq(" + $scope.selectedCol + ") span").hasClass("empty")) {
            $("tr:eq(" + $scope.selectedRow + ") td:eq(" + $scope.selectedCol + ") span").html(number);
            $scope.sudokuGrid[row][col] = number + "!";
            $scope.checkCurrentNumber(row, col);
            $scope.getCurrentNumber(row, col);

            // if inserted number is correct add class
            if ($scope.getValue === $scope.sudokuGridSolved[row][col]) {
                console.log("Correct");
                $("tr:eq(" + $scope.selectedRow + ") td:eq(" + $scope.selectedCol + ")").removeClass("incorrect");
                $("tr:eq(" + $scope.selectedRow + ") td:eq(" + $scope.selectedCol + ")").addClass("correct");
            }
            // add incorrect class if inserted number is not correct
            else {
                console.log("Incorrect!");
                $("tr:eq(" + $scope.selectedRow + ") td:eq(" + $scope.selectedCol + ")").removeClass("correct");
                $("tr:eq(" + $scope.selectedRow + ") td:eq(" + $scope.selectedCol + ")").addClass("incorrect");
            }
        }
    }
    $scope.checkForIdenticalValues();
}

所以问题是因为 ng-keydown<td> 标签上,所以只有当它是当前聚焦的元素时才会调用该函数,但听起来你想响应按下键来自页面任意位置的事件。

您可以通过在 body 标签上使用 ng-keydown 而不是 <td> 来做到这一点,然后让函数广播一个可以从数独组件监听的事件到 运行 insertNum 逻辑。像这样:

<body ng-keydown="onMyKeydownEvent($event)"> 

然后你在run()内绑定了$rootScope上的函数来广播一个事件。

app.run(function($rootScope) {
  $rootScope.onMyKeydownEvent = function(e) {
    $rootScope.$broadcast("MyKeydownEvent", e);
  };
});

之后将侦听器添加到您的组件。

$scope.$on("MyKeydownEvent", function(e) {
  // insertNum logic goes here.
});

您还需要对逻辑进行一些调整,因为 e.currentTarget$scope.selectedCol = col; $scope.selectedRow = row; 等内容将不再按预期工作。相反,<td> 标签上的 ng-click="selectedCell(row, col)" 应该负责将所需的单元格数据设置到范围中,以便它可以在事件中使用。