Struggling to make addClass and removeClass function correctly for the development of the unique "15 puzzle" game

I am currently in the process of creating a version of "the 15 game" using jQuery in HTML. You can find more information about the game on this page. The objective of the game is to rearrange a table of "boxes" in ascending order from 1 to 15.

Below is the code I have implemented:

// Populating the array with numbers from 1 to arraysize
function insertElements(myArr, arraysize){
for (var i = 1; i < arraysize; i++ ) {
    myArr.push(i);
}
}

// Checking if two cells are within range for movement
function canMove(col, row, empty_row, empty_col){
if((row == empty_row+1) && (col == empty_col) ||
(row == empty_row-1) && (col == empty_col) ||
(row == empty_row) && (col == empty_col+1) ||
(row == empty_row) && (col == empty_col-1)){
return true;
}
else
return false;
}

// Swapping elements in the array
function swapElements(myArr, indexA, indexB){
// Check bounds
if((indexA > myArr.length) || (indexA < 0) || (indexB > myArr.length) || (indexB < 0)){
alert("Out of bounds");
} 
else{

var temp = myArr[indexA];
myArr[indexA] = myArr[indexB];
myArr[indexB] = temp;

}
}

// Waiting for the page to finish loading
$(document).ready(function(){ 

// Creating array
var myArr = [];
var arraysize = 17;
var lastelement = arraysize-1;
var empty_row;
var empty_col;
var empty_cell;

var col;
var row;
var nonempty_cell;

// Inserting the elements
insertElements(myArr, arraysize);

// Number of shuffles
var shuffleNum = 10000;

 // Shuffling the array
for (var i = 0; i < shuffleNum; i++ ) {
var f = Math.floor((Math.random()*16)+0);
var s = Math.floor((Math.random()*16)+0);
swapElements(myArr, f, s);
}

i = 0;
// For each td element in the table
$(".maincontainer td").each(function() {
// Getting a random value from the array
val = myArr[i];

// If the value is the last element
// assigning this cell as the empty cell and adding the class 'empty'
if(val == lastelement)
{
empty_cell = $(this);
empty_cell.addClass("empty");
empty_row = this.parentNode.rowIndex;
empty_col = this.cellIndex;
}
// Otherwise, assigning the value val to its text and adding the class 'nonempty' to it
else
{
$(this).text(val);
$(this).addClass("nonempty");

    }

    ++i;
    
});

// When one of the nonempty boxes is clicked
$(".nonempty").click(function(){

// Assigning the cell that has been clicked to the nonempty cell
nonempty_cell = $(this);
row = this.parentNode.rowIndex; 
col = this.cellIndex;

// If the cell is in range of the empty cell
if(canMove(col, row, empty_row, empty_col)){

// Swapping the empty cell and the non-empty cell
var temp = empty_cell;
empty_cell = nonempty_cell;
nonempty_cell = temp;

// Swapping coordinates
var temprow = row;
row = empty_row;
empty_row = temprow;
var tempcol = col;
col = empty_col;
empty_col = tempcol;

// Getting text from the old empty cell
var new_value = $(empty_cell).text();

// Assigning the value to the nonempty cell text and changing the class to nonempty
$(nonempty_cell).text(new_value);
$(nonempty_cell).removeClass("empty").addClass("nonempty");

// Clearing the text and changing the class to empty
$(empty_cell).removeClass("nonempty").addClass("empty");
$(empty_cell).text("");
}
else
{
alert("Cannot move!");
}
});  


$(".empty").click(function(){
alert("Clicked on an empty cell!");

});
});
<!DOCTYPE html>
<html lang='sv'>
<head>
<meta charset="UTF-8" >
<title>The 15 Game</title>

<style>

.maincontainer
{
width: 35%;
border: 10px solid black;
}

.maincontainer td
{
width: 100px;
height: 100px;
text-align: center;
font-size: 100px;
border: 3px solid black;
}

.nonempty
{
background-color: red;
}

.empty
{
background-color: #C0C0C0;
}

.nonempty:hover
{
border: 3px solid white;
}
</style>


<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

<!-- External javascript file containing the game logic -->
<script type="text/javascript" src="javascript/15game.js"></script>

</head>
<body>

<!-- Table serving as the main container for the 16 boxes -->
<table class="maincontainer" >
  <tr>
    <td></td>
    <td></td> 
    <td></td>
    <td></td>
  </tr>
  <tr>
    <td></td> 
    <td></td>
    <td></td>
    <td></td>
  </tr>
  <tr>
    <td></td>
    <td></td> 
    <td></td>
    <td></td>
  </tr>
  <tr>
    <td></td>
    <td></td> 
    <td></td>
    <td></td>
  </tr>
</table>

</body>
</html>

The functionality of the code is mostly working properly. Clicking on a cell that is within range of an empty cell successfully swaps the two cells. However, there seems to be an issue where the cell initially assigned the class "empty" retains this class when trying to change it to "nonempty."

Please forgive any errors or typos, English is not my first language. Any assistance would be greatly appreciated!

Answer №1

It's important to note that when you bind events to elements based on their classes, those event bindings are specific to the classes at the time of binding. If the classes change later on, the event bindings won't automatically update.

An alternative approach is to use delegated events, which can dynamically detect the class at the time of the event triggering:

$(".maincontainer").on("click", ".nonempty", function(){

and

$(".maincontainer").on("click", ".empty", function(){

If interested, here's a demo showcasing this in action:

// Function to insert numbers into an array
function insertElements(myArr, arraysize){
for (var i = 1; i < arraysize; i++ ) {
    myArr.push(i);
}
}

// Check if two cells are within range for moving
function canMove(col, row, empty_row, empty_col){
if((row == empty_row+1) && (col == empty_col) ||
(row == empty_row-1) && (col == empty_col) ||
(row == empty_row) && (col == empty_col+1) ||
(row == empty_row) && (col == empty_col-1)){
return true;
}
else
return false;
}

// Swap elements within an array
function swapElements(myArr, indexA, indexB){
// Check bounds
if((indexA > myArr.length) || (indexA < 0) || (indexB > myArr.length) || (indexB < 0)){
alert("Out of bounds");
} 
else{

var temp = myArr[indexA];
myArr[indexA] = myArr[indexB];
myArr[indexB] = temp;

}
}  

// Wait for page to fully load
$(document).ready(function(){ 

// Create array
var myArr = [];
var arraysize = 17;
var lastelement = arraysize-1;
var empty_row;
var empty_col;
var empty_cell;

var col;
var row;
var nonempty_cell;

// Insert elements into the array
insertElements(myArr, arraysize);

// Number of shuffles
var shuffleNum = 10000;

// Shuffle the array randomly
for (var i = 0; i < shuffleNum; i++ ) {
var f = Math.floor((Math.random()*16)+0);
var s = Math.floor((Math.random()*16)+0);
swapElements(myArr, f, s);
}

i = 0;
// Loop through each td element in the table
$(".maincontainer td").each(function() {
// Get a random value from the array
val = myArr[i];

// Check if the value is the last element and assign appropriate class
if(val == lastelement)
{
empty_cell = $(this);
empty_cell.addClass("empty");
empty_row = this.parentNode.rowIndex;
empty_col = this.cellIndex;
}
// Otherwise, set text value and add nonempty class
else
{
$(this).text(val);
$(this).addClass("nonempty");

    }

    ++i;
    
});

// Event handler for clicking on nonempty boxes
$(".maincontainer").on("click", ".nonempty", function(){

// Keeps track of clicked nonempty cell
nonempty_cell = $(this);
row = this.parentNode.rowIndex; 
col = this.cellIndex;

// Verify if selected cell is within range of empty cell for movement
if(canMove(col, row, empty_row, empty_col)){

// Swap empty cell with nonempty cell
var temp = empty_cell;
empty_cell = nonempty_cell;
nonempty_cell = temp;

// Update coordinates
var temprow = row;
row = empty_row;
empty_row = temprow;
var tempcol = col;
col = empty_col;
empty_col = tempcol;

// Retrieve text from old empty cell
var new_value = $(empty_cell).text();

// Assign new value to nonempty cell and adjust classes
$(nonempty_cell).text(new_value);
$(nonempty_cell).removeClass("empty").addClass("nonempty");

// Reset text and classes for empty cell
$(empty_cell).removeClass("nonempty").addClass("empty");
$(empty_cell).text("");
}
else
{
alert("Unable to move!");
}
});  


// Event handler for clicking on empty cell
$(".maincontainer").on("click", ".empty", function(){
alert("Clicked on empty cell!");

});
});
<!DOCTYPE html>
<html lang='sv'>
<head>
<meta charset="UTF-8" >
<title>The 15 game</title>;

<style>

.maincontainer
{
width: 35%;
border: 10px solid black;
}

.maincontainer td
{
width: 100px;
height: 100px;
text-align: center;
font-size: 100px;
border: 3px solid black;
}

.nonempty
{
background-color: red;
}

.empty
{
background-color: #C0C0C0;
}

.nonempty:hover
{
border: 3px solid white;
}
</style>


<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

<!-- External javascript with the game -->
<script type="text/javascript" src="javascript/15game.js"></script>

</head>
<body>

<!-- Table which is the main container for the 16 boxes -->
<table class="maincontainer" >
  <tr>
    <td></td>
    <td></td> 
    <td></td>
    <td></td>
  </tr>
  <tr>
    <td></td> 
    <td></td>
    <td></td>
    <td></td>
  </tr>
  <tr>
    <td></td>
    <td></td> 
    <td></td>
    <td></td>
  </tr>
  <tr>
    <td></td>
    <td></td> 
    <td></td>
    <td></td>
  </tr>
</table>

</body>
</html>

Answer №2

The issue you're facing stems from the jQuery line below:

$(".empty").click(function(){
    alert("Clicked empty cell!");
});

This line targets all elements with the class '.empty' and attaches a click event handler to them. However, when you change classes on one element, the event handler remains unchanged.

To solve this problem, consider adding an event handler with a broader scope, such as the table or the document. Then, check if the event target has the specific class before executing the desired action.

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

How can mongoose be used to modify user information in a database?

Hey there, I'm currently working on a personal project and came across an issue. I've successfully set up my (steam) user data to save in mongodb through mongoose. Right now, I have it set to only save a new user if they are not already in the da ...

Proper Structure for Node System (BASIC)

Overview Recently, I entered the world of Node.js and built some basic back end functionality. However, I realized that everything was clustered in one file (index.js) which led me to explore tutorials on using express router middleware and adapting a mod ...

Parked domain with active Ajax functionality

My current project involves writing jQuery code, and I'm facing an issue where it works on the normal domain but not on the parked domain. Normal domain - Parked domain - If you scroll down to the colony news section and click on the link, you&apos ...

Unable to locate child after adding element to HTML

I have been attempting to add a child element to my HTML document, and although I can see it in the source code, it doesn't display on the screen as expected. It's quite baffling. Here is the HTML code snippet: <!DOCTYPE html> <html> ...

Deleting Empty Session Data

I have set up sessions for my website. In order to initialize the session, I included the following code on every link of the website: session_start(); if(isset($_SESSION['User'])) { //session_start(); $sesvar = $_REQUEST['sid']; } ...

Transfer data from a Django template populated with items from a forloop using Ajax to a Django view

Struggling to implement a click and populate div with ajax in my django e-commerce app. The function involves clicking on a category in the men's page which then populates another div. gender.html {%for cate in cat%} <a href="javascript:getcat()" ...

Passport is implementing multistrategies for multiple authentications simultaneously

After configuring Passport.js to utilize multiple strategies, I encountered an issue: passport.authenticate(['bearer', 'facebook-token', 'google-token', 'linkedin-token'],function(err, user, info) ... Although it i ...

Transforming dates in JavaScript

So I have a situation where I need to call php from javascript. The URL address is Jun 18 18:00:00 UTC+0200 in the year 2013, but that format is not suitable for my needs. I want to convert it to the format YYYY-MM-DD, either using JavaScript or PHP. An ...

Find the variance between the sums of columns 3 and 5 using Angular

Is there a method to calculate the variance between column 3 and the last one in a table? <body ng-app="Test"> <section style="margin-top:80px"> <h3>Plastic Calculator Form Version 2.0</h3> <div ng-controller="TestCo ...

Concerning dilemma with a div element

I am using 2 different partial views in my main view: <div class="col-md-6"> @Html.Action("Chart", "Teams") </div> <div class="col-md-6"> @Html.Action("SeriesWinsChart", "Teams") </div> <div class="next-game"> & ...

javascript popup appears twice in IE when using PHP

After testing this script on multiple browsers, an issue arises when using IE. In all other browsers, the form submission alert only appears once. However, in Internet Explorer, the alert pops up twice. What could be causing this discrepancy? <!DOCTYP ...

I am looking for a way to access an array from Node.js using JavaScript and EJS. How can I achieve this

Currently, I am developing an app that requires passing an array from the server to the client. Initially, I attempted the following: // Server app.get('/', (req,res) => { res.render('index', { data: ["Hello"] }) }) ...

Which data types in JavaScript have a built-in toString() method?

Positives: 'world'.toString() // "world" const example = {} example.toString() // "[object Object]" Negatives: true.toString() // throws TypeError false.toString() // throws TypeError Do you know of any other data types that wi ...

What sets id and class apart from each other?

I understand that for id we need to use the '#' symbol and for class '.' symbol. However, I am unsure of the specific purposes of both class and id in styling CSS. Can someone provide clarification on when I should use id and when I sho ...

Transforming identical elements in an arrayt array into distinct elements without eliminating any element in Javascript

Looking for a solution to remove duplicates from an array? You may have come across using track by $index in ng-repeat of angularjs, but you want to stick with a regular ng-repeat. Here's the scenario: Array=[ { name:'john',salary:& ...

Locate the position of a substring within a Uint8Array

I'm working with a Uint8Array that contains the content of a PDF file. My goal is to locate a specific string within this array in order to insert additional content at that particular position. My current approach involves converting the Uint8Array ...

Update the network name in the Axios request

Currently, I am utilizing Axios for handling both GET and POST requests. One question that has been on my mind is how to modify the network name: At present, the name serves as the parameter accompanying each request. However, I ponder whether it's f ...

Utilizing Asp.Net in C#, Incorporate GridView within a jQuery Dialog

I am attempting to dynamically display a DataGridView using a function. Let's dive into the code: HtmlGenericControl div = new HtmlGenericControl("div"); div.ID = Guid.NewGuid().ToString(); GridView grid = new GridView(); grid.Attributes.Add("runat" ...

Vertical alignment of buttons in Cordova App Rate

Currently, I am utilizing phonegap along with the app rate plugin. When I execute the code: AppRate.promptForRating(true); All buttons are appearing in a singular line. Despite there being 3 buttons available, the text on the buttons is getting truncate ...

Troubleshooting issue: Rails application custom action with Cancancan and Ajax not

After integrating Devise and Cancancan for authentication and authorization in my Rails App, I encountered an issue where a previously functioning action now redirects to the home page when called from the JavaScript form submission. The JavaScript code s ...