简体   繁体   中英

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

I am building a sudoku game in AngularJS. What I want to achieve is to catch ng-keypress even if I click outside of the div on the page. Take a look at http://sudoku.com/ for example if you select a cell and then click wherever you want on the page and after that, if you click on any number still it is going to change that number in the cell. How can I achieve that?

Please take a look at the HTML code below:

<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>

On ng-keydown I am firing this function:

// 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();
}

So the issue is because ng-keydown is on the <td> tag so the function will only be called when it's the currently focused element, but it sounds like you want to respond to the key down event from anywhere on the page.

You can do this by using ng-keydown on the body tag instead of the <td> , then make the function broadcast an event that can be listened to from the sudoku component to run the insertNum logic. Like so:

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

Then you bound the function on the $rootScope within run() to broadcast an event.

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

After that add the listener to your component.

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

You will also need to make some adjustments to the logic as things like e.currentTarget and $scope.selectedCol = col; $scope.selectedRow = row; will no longer work as expected. Instead the ng-click="selectedCell(row, col)" on the <td> tag should be responsible for setting the required cell data into the scope so that it can be used within the event.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM