display-name
display-name

Reputation: 1

Why does defining a variable outside a function produce different behavior than when defined inside a function? Scoping related?

I'm relatively new to JavaScript and in this program I encountered some strange behavior that I would like to understand.

Some context: resetButton is a button to reset the elements in boxes to their initial state. As part of this process, I clone all the elements in boxes to remove their event listeners. Next, I would like to re-add the event listeners to the new elements. This is where the problem arises.

In the first code snippet, boxes is defined outside the initializeBoxes and the addEventListener for resetButton and it seems like I am unable to add event listeners to the elements in boxes

In the second code snippet, boxes is defined inside each function and this code works fine. It looks like the event listeners are cleared and re-added when initalizeBoxes is called.

I think this has to do with scope, however I thought that initalizeBoxes's closure would capture boxes from outside the function. This is where I can't understand what is really happening. Any insights would be greatly appreciated!

let boxes = document.querySelectorAll('.box');
    
let resetButton = document.getElementById('reset');
resetButton.addEventListener('click', () => {
    boxes.forEach((box) => {
        box.replaceWith(box.cloneNode(true));
    });
    initalizeBoxes();
});

function initalizeBoxes() {
    boxes.forEach((box) => {
        box.addEventListener('click', () => {
            // do stuff
let resetButton = document.getElementById('reset');
resetButton.addEventListener('click', () => {
    let boxes = document.querySelectorAll('.box');
    boxes.forEach((box) => {
        box.replaceWith(box.cloneNode(true));
    });
    initalizeBoxes();
});

function initalizeBoxes() {
    let boxes = document.querySelectorAll('.box');
    boxes.forEach((box) => {
        box.addEventListener('click', () => {
            // do stuff

Upvotes: 0

Views: 31

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370809

In the first snippet, the boxes variable refers to the collection of elements matching .box that existed at the moment that line was run - let boxes = document.querySelectorAll('.box'); - so, those elements when the page was loaded. The first time the button is clicked, boxes.forEach((box) => { box.replaceWith may properly iterate over all those elements. But then if you click the button again sometime, all elements in the boxes variable refer to elements no longer in the document, so calling .replaceWith on them doesn't affect anything in the current DOM.

In contrast, if you select the boxes inside the button's click listener, you'll be selecting all boxes that exist in the DOM at that point, so you'll be guaranteed to have an up-to-date collection to iterate over and replace.

Upvotes: 2

Related Questions