1

I'm working on a HTML / Javascript project to create a simple game of Minesweeper. In order to create a clickable grid of cells, I adopted code that I found in another thread. That code works perfectly on its own (see this JSBin for more details):

var lastClicked;
var grid = clickableGrid(10,10,onClick);

document.body.appendChild(grid);

// This is the clickableGrid object constructor.  It takes in the number of rows and columns
// for the grid, and a callback function that will be executed when a cell is clicked
function clickableGrid( rows, cols, callback ){
    // 'i' is the variable that will store the number in each cell.  A 10x10
    // grid, for example, will see i range from 1 (top left cell) to 100 (bottom right cell)
    var i=0;

    // Create the table element and assign it a class name so that CSS formatting
    // may be applied
    var grid = document.createElement('table');
    grid.className = 'grid';

    // Build the grid row by row
    for (var r=0;r<rows;++r){

        // Create a table row element and append it to the table element ('grid')
        var tr = grid.appendChild(document.createElement('tr'));
        tr.row = r;

        // Build the row cell by cell
        for (var c=0;c<cols;++c){

            // Create the cell element and append it to the row element ('tr')
            var cell = tr.appendChild(document.createElement('td'));

            // Input the number to the cell's innerHTML
            cell.innerHTML = ++i;

            // Add an event listener that will execute the callback function
            // when the cell is clicked, using the cell's element and information
            cell.addEventListener('click',(function(el, r, c, i){
                return function() {
                    callback(el, r, c, i);
                }
            })(cell, r, c, i),false);
        }
    }
    return grid;
}

// This function contains the actions we want to be executed when the click occurs
function onClick(el, row, col, i) {
    // Log to the console the details of the cell that was clicked
    console.log("You clicked on element:",el);
    console.log("You clicked on row:",row);
    console.log("You clicked on col:",col);
    console.log("You clicked on item #:",i);

    // Record in the element that it was clicked
    el.className='clicked';

    // If the element is not the same as 
    if (lastClicked) lastClicked.className='';
    lastClicked = el;
}

However, I can't seem to make it work properly in my Minesweeper game. The "mined" grid gets built and appended to the DOM, but the innerHTML and listeners are not applied. This other JSBin contains all the game code I have so far. The current process is:

1) Run init to initialize the page and create all elements. Part of this includes adding an event listener to the "New Game" button.

2) When the "New Game" button is clicked, create a clickable grid that will be "mined". At the moment, no mines get placed, but the code tries to place an "X" inside each cell. Furthermore, each cell should have an event listener attached to it.

The relevant code section in the game is:

function startGame() {
    var gameDifficulty = document.getElementsByTagName("select")[0].value;
    var currGrid = document.querySelector('.grid');
    var newGrid = new minedGrid(gameDifficulty, onClick);
    currGrid.parentNode.replaceChild(newGrid, currGrid);
}

// minedGrid object constructor: creates and returns a fully-mined and
// prepared Minesweeper grid
function minedGrid(difficulty, callback){
    var rows, cols, mines;

    var newGrid = document.createElement('table');
    newGrid.className = 'grid';

    switch (difficulty) {
        case 0:
            rows = 10;
            cols = 10;
            mines = 10;
            break;
        case 1:
            rows = 16;
            cols = 16;
            mines = 40;
            break;
        case 2:
            rows = 16;
            cols = 30;
            mines = 99;
            break;
        default:
            rows = 10;
            cols = 10;
            mines = 10;
            break;
    }

    for (var r = 0; r < rows; ++r) {
        var tr = newGrid.appendChild(document.createElement('tr'));

        for (var c = 0; c < cols; ++c) {
            var cell = tr.appendChild(document.createElement('td'));
            cell.innerHTML = "X";
            cell.addEventListener('click',(function(el, r, c){
                return function() {
                    callback(el, r, c);
                }
            })(cell, r, c),false);
        }
    }
    return grid;
}

// This function contains the actions we want to be executed when the click occurs
function onClick(el, row, col) {
    // Log to the console the details of the cell that was clicked
    console.log("You clicked on element:",el);
    console.log("You clicked on row:",row);
    console.log("You clicked on col:",col);

    // Record in the element that it was clicked
    el.className='clicked';

    // If the element is not the same as 
    if (lastClicked) lastClicked.className='';
    lastClicked = el;
    }
Cœur
  • 37,241
  • 25
  • 195
  • 267
ricardo_s
  • 75
  • 1
  • 1
  • 6
  • 2
    I had to stop by to compliment you on your code comments... :) – Salketer Aug 28 '17 at 14:44
  • Took me some time to find the issue. The new grid you're creating (with X's and click handlers) is never appended to the document. –  Aug 28 '17 at 15:01
  • Here's a revised version that focuses on creating the grid: http://jsbin.com/yaruwevuzi/edit?html,css,js,output –  Aug 28 '17 at 15:17

1 Answers1

0

Solution by OP.

Chris G, who commented on the question, was absolutely correct when noting that the grid is "never appended to the document." The issue is that the minedGrid function has return grid; at the bottom, when it should do return newGrid; instead. If you would like to see a revised version of this code, please refer to this link.

I want to again specifically highlight Chris G for his help, as well as his version of the minedGrid code: Chris G's JSBin.

Cœur
  • 37,241
  • 25
  • 195
  • 267