Jonathan Hinds
Jonathan Hinds

Reputation: 305

button.addEventListener not working in nested for loop

I'm working on an engine that creates maps files for a game I made. The game is a text based adventure that uses a grid of spaces to display events like messages, items, quests, enemies etc.

The form I'm working on now will create a grid using buttons as the values are type into the input fields - this creates a better visual representation.

I'm working on this function that does the following.

  1. checks if a height has been specified - this way it knows if its drawing a linear room or a rectangular room before anything else is execute

  2. if it's a linear room (only width has been specified), it uses the width to create and append buttons to the containing element.

  3. if it is rectangular (width and height have been specified), a nested for loop runs which appends x amount of buttons for the width, then y amount of
    's for the height.

  4. when one of these buttons is clicked, it appends another form to the screen. which can then be filled out by the user.

Here is the code for this:

    var creategridwidth = function(room)
{
    //store the room in a local variable
    var room = room;
    //while the grid container has children in it
    while(room.subGridContainer.hasChildNodes())
    {
        //remove the children from the container
        room.subGridContainer.removeChild(room.subGridContainer.lastChild);
    }
    //if the room height has already been set
    if(room.heightValue > 0)
    {
        //loop through the amount of rows
        for(var x = 0; x < room.heightValue; x ++)
        {
            //loop through the amount of columns
            for(var y = 0; y < room.widthValue; y ++)
            {
                //create a new button element
                var button = document.createElement('button')
                //assign the button element to the grid class
                button.setAttribute("class", "grid");
                //add an event listener for on click to view input information
                button.addEventListener('click', function() 
                {
                    console.log("click")
                    //run add space input function to add the html form to the page.
                    addspaceinput(room);
                }, false);
                //display the button as inline block so that they are side by side
                button.style.display = "inline-block";
                //append the button to the grid container
                room.subGridContainer.appendChild(button);                          
            }
            //when the row has been added, add a line break to start the next lie.
            room.subGridContainer.innerHTML += "<br>";
        }
    //if the height has not already been set
    } else {
        //loop through the width of the room
        for(var z = 0; z < room.widthValue; z ++)
        {
            //create a new button element
            var button = document.createElement('button')
            //assign the button element to the grid class
            button.setAttribute("class", "grid");
            //add an event listener for on click to view input information
            button.addEventListener('click', function() 
            {
                console.log("click")
                //run add space input function to add the html form to the page.
                addspaceinput(room);
            }, false);
            //display the button as inline block so that they are side by side
            button.style.display = "inline-block";
            //append the button to the grid container
            room.subGridContainer.appendChild(button);
        }
    }
}

Now, the part I'm having trouble with is the event listener happening in the nest for loop.

For example:

        for(var z = 0; z < room.widthValue; z ++)
        {
            //create a new button element
            var button = document.createElement('button')
            //assign the button element to the grid class
            button.setAttribute("class", "grid");
            //add an event listener for on click to view input information
            button.addEventListener('click', function() 
            {
                console.log("click")
                //run add space input function to add the html form to the page.
                addspaceinput(room);
            }, false);
            //display the button as inline block so that they are side by side
            button.style.display = "inline-block";
            //append the button to the grid container
            room.subGridContainer.appendChild(button);
        }

I can see this log appear in the console, this works fine. However:

        for(var x = 0; x < room.heightValue; x ++)
        {
            //loop through the amount of columns
            for(var y = 0; y < room.widthValue; y ++)
            {
                //create a new button element
                var button = document.createElement('button')
                //assign the button element to the grid class
                button.setAttribute("class", "grid");
                //add an event listener for on click to view input information
                button.addEventListener('click', function() 
                {
                    console.log("click")
                    //run add space input function to add the html form to the page.
                    addspaceinput(room);
                }, false);
                //display the button as inline block so that they are side by side
                button.style.display = "inline-block";
                //append the button to the grid container
                room.subGridContainer.appendChild(button);                          
            }
            //when the row has been added, add a line break to start the next lie.
            room.subGridContainer.innerHTML += "<br>";
        }

This gives absolutely no behavior. I'm not sure how to move forward with this. I'd appreciate any suggestions on this.

Upvotes: 1

Views: 290

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370689

The problem is here:

room.subGridContainer.innerHTML += "<br>";

When you assign to innerHTML, the existing innerHTML is retrieved, the element is cleared, and then the new concatenated innerHTML string is parsed into the DOM. So, any variables referencing elements inside will be lost, including elements that used to have event listeners.

const button = document.body.appendChild(document.createElement('button'));
button.textContent = 'click';
button.onclick = () => console.log('clicked');

// the next line will break the listener:
document.body.innerHTML += '<br>';

Explicitly append elements instead:

room.subGridContainer.appendChild(document.createElement('br'));

Another option would be to insertAdjacentHTML:

room.subGridContainer.insertAdjacentHTML('beforeend', '<br>');

But if you want the button to be on its own line, it would be better to put it inside a block element, rather than append brs:

var div = room.subGridContainer.appendChild(document.createElement('div'));
var button = div.appendChild(document.createElement('button'));
// etc

Upvotes: 1

Related Questions