What is the best way to capture ng-keypress/ $event when the user clicks outside of a div?

I am currently working on developing a Sudoku game using AngularJS. My goal is to capture the ng-keypress event even when clicking outside of the div on the page. An example of this behavior can be seen at . If you select a cell and then click anywhere else on the page, you can still change the number in the selected cell by clicking on any number. How can I implement this functionality?

Here's an excerpt of the HTML code:

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

The function triggered on ng-keydown is as follows:

// 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; // return false if clicked button/event is not a number or delete/backspace button

        if (e.currentTarget.children[0].classList[2] === 'empty') // check if clicked cell is empty
            if (keyCode === 8 || keyCode === 46) { // remove current value if delete/backspace is clicked
                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 { // insert number in cell when number is clicked
                e.currentTarget.children[0].innerHTML = e.key;
                $scope.sudokuGrid[row][col] = e.key + "!";
                $scope.checkCurrentNumber(row, col);
                $scope.getCurrentNumber(row, col);

                // add correct class if inserted number is correct
                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");
                }
            }
    }
    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);

            // add correct class if inserted number is correct
            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();
}

Answer №1

It seems that the issue arises because the ng-keydown is placed on the <td> tag, which means the function will only trigger when that element is currently focused. If you want to respond to key down events from anywhere on the page, you can achieve this by attaching the ng-keydown to the body tag instead of the <td>. Then, have the function broadcast an event that can be picked up by the sudoku component to execute the insertNum logic.

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

Next, define the function on the $rootScope in the run() block to broadcast the event.

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

Add a listener for this event in your component.

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

Please note that adjustments need to be made to some parts of the logic, such as utilizing e.currentTarget and updating variables like $scope.selectedCol = col; and $scope.selectedRow = row;. Instead, consider using the

ng-click="selectedCell(row, col)"
directly on the <td> tag to manage setting the necessary cell data in the scope for event handling.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The screen reader seems to be malfunctioning as soon as I include this particular code

//Finding the height of the header let headerHeight = document.querySelector('header'); let height = headerHeight.offsetHeight; //Adjusting the #navbarNav's top margin to accommodate the header let nn = docu ...

Creating a nested list component using an array of objects

Seeking guidance for a coding task I recently completed. I was tasked with creating a multiple nested list from an array of objects. While I achieved the expected result, my code ended up being overly complicated and not very clean. I used a combination of ...

What is the best way to create a visual separation between these two arrows using styled-components?

Currently, I am immersing myself in styled-components and attempting to replicate the image provided below. Although I can achieve this with CSS, I am solely focusing on utilizing styled components for this task. <Container> {sliderItems.map((item) ...

Verify user identity before sending directory in Express

I'm encountering an issue with authenticating users before they access an express directory file tree. While I can successfully authenticate users on all other pages, I'm facing difficulties with authentication on "/dat/:file(*)" even though I ha ...

Leveraging weather application programming interfaces

I am trying to set up a basic webpage that can display tide information from wunderground.com. However, for some reason I am not seeing any results on the page. I have included the load function in hopes of at least getting something to appear when the p ...

Exploring a new path with Angular

I'm attempting to dynamically change the sp-right class to sp-left in Angular: Html <span class="sp-right"> <label> Number: </label> </span> Directive app.directive("buttonThatTrigger", function () { ...

Learn a valuable trick to activate CSS animation using a button - simply click on the button and watch the animation start over each time

I already know how to do this once. However, I would like the animation to restart or begin again when the user clicks on it a second time. Here is what I have: function animation() { document.getElementById('ExampleButton').className = &apo ...

I am experiencing a lack of results when attempting to run db.find() in Mongodb

Recently I delved into the realm of MongoDB, deciding to create a basic application that simply showcases data stored in my database. Check out the code snippet below: var mongoose = require("mongoose"); mongoose.connect("mongodb://localhost ...

Is there a way to ensure that both new Date() and new Date("yyyy-mm-dd hh:mm:ss") are initialized with the same timezone?

When utilizing both constructors, I noticed that they generate with different timezones. Ideally, they should be in the same timezone to ensure accurate calculations between them. I attempted to manually parse today's date and time, but this feels li ...

Retrieve the data attribute from a select box that was created dynamically

Following an AJAX request, I have successfully generated two select boxes: $("#job_id").change(function() { var id = $(this).val(); $.ajax({ url: '', type: 'POST', dataType: 'json', dat ...

Upon returning to the previous page, the checkbox remains checked and cannot be unchecked

Here is the code I'm using to append checkbox values with a hash in the URL. However, when navigating back, the checkboxes remain checked. Take a look at the code snippet below: <html> <head> <script src="http://ajax.googleapis.com/aja ...

Obtain the textfield whenever the user desires

I have added an image upload file field in the form. If the user wants a multi-select field, I want to allow the user to choose the number of files they want to upload. Take a look at my text field below: <div class="form-group col-lg-10"> {! ...

Using AngularJS to Extract JSON Data from a Table in an HTML Document

In the context of angularjs: I have a table with 2 fixed columns (ID & Comment) and additional columns that are added dynamically by the user clicking a button. The user can input data into the rows of this table, and I need to extract/ read this data. H ...

AngularJS, building a hash of resources

Is there a way, in an AngularJS controller, to take a URL and redirect that request to the best place for fetching JSON data? The VideoSearchCtrl is connected to the search form. Everything seems fine with the generated URL for the template, so I aim to us ...

When the web driver fails to function as expected

After installing the selenium-webdriver via npm, I downloaded the IE component from this link and added it to my path on Windows 8. Upon opening IE, I had to set all security zones to high, ensuring they were consistent. However, due to restrictions in th ...

Tips on incorporating a parameter into an API call function within a React application

I'm a newcomer to the world of React and trying to figure things out as I go. Here is the code snippet I have: const handleDelete = e => { const token = getCookie('token'); e.preventDefault(); axios .delete( `$ ...

Using HTML5 video with cue points and stop points

I've noticed that there are various options available for implementing cuepoints in HTML5 videos, such as using PopcornJS or CuepointsJS to trigger play events at specific times within the video track. However, I am wondering if there is a solution t ...

I am encountering a problem with the app.patch() function not working properly. Although the get and delete functions are functioning as expected, the patch function seems to be

I am in the process of setting up a server that can handle CRUD operations. The Movie model currently only consists of one property, which is the title. Although I can create new movies, delete existing ones, and even search for a ...

When a webpage is moved, the globalProperties variable of "vue3 typescript" is initialized to null

main.ts const app = createApp(App) .use(router) .use(createPinia()) .use(vuetify) .use(vue3GoogleLogin, googleLogin) const globalProps = app.config.globalProperties; globalProps.isDebugMode = true; vue-shim declare ...

What is the best way to convert the data stored in an object to an array?

I have a function that is constantly checking for temperature data: {"a":"43", "b":"43", "c":"42", "d":"43", "e":"40", "f":"41", "g":"100", "h":"42.6"} My goal is to graph this data over time, but I'm struggling with how to structure it to fit the f ...