Search code examples
javascriptfunctionscopelexical-scope

Changing Scope from Global to Local Breaking Javascript Program


Thanks to the help of you fine Overflowians, I fixed up my silly little RNG Addition game and got it working. Now, at one user's suggestion, I'm trying to change the scope of the addition game's code from global to local before I code up the next game; I want each game to be completely contained within its own scope, as I understand that learning to not thoughtlessly contaminate the global scope is a good idea. However, I'm a bit stuck on how to achieve that.

Here's the code for the currently functional addition game:

function beginAdditionChallenge() {
    var x = Math.ceil(Math.random()*100);
    alert(x);
    for (var i = 0; i < 3; i++) {
        var a = Number(prompt("Provide the first addend.", ""));
        var b = Number(prompt("Provide the second addend.", ""));
        if (a + b === x) {
            alert("Well done!");
            break;
        }
            else if (a + b !== x && i < 2) {
            alert("Please try again.");
        }
        else {
            alert("Derp.");
        }
    }
}

function initChallenge() {
    var button = document.getElementById("challengeButton");
    button.addEventListener("click", beginAdditionChallenge);
}

window.addEventListener("load", initChallenge);

And here's my attempt to wrap it, which only succeeds in breaking the game, such that the button doesn't even respond:

window.addEventListener("load", function() {
    function beginAdditionChallenge() {
        var x = Math.ceil(Math.random()*100);
        alert(x);
        for (var i = 0; i < 3; i++) {
            var a = Number(prompt("Provide the first addend.", ""));
            var b = Number(prompt("Provide the second addend.", ""));
            if (a + b === x) {
                alert("Well done!");
                break;
            }
                else if (a + b !== x && i < 2) {
                alert("Please try again.");
            }
                else {
                alert("Derp.");
            }
        }
    }

    function initChallenge() {
        var button = document.getElementById("challengeButton");
        button.addEventListener("click", beginAdditionChallenge);
    }

    window.addEventListener("load", initChallenge);
    });

Functional code is available on JSFiddle here.


Solution

  • Note that the onLoad option in JSFiddle does the same as your 2nd snippet. You'll want to choose one of the No wrap options when binding to 'load' yourself.

    And, the issue stems from attempting to bind to 'load' within a 'load' handler:

    window.addEventListener("load", function() {
        // ...
    
        window.addEventListener("load", initChallenge);
    });
    

    When the event is already firing and handling the outer binding, it's too late to add more handlers to it. They won't be cycled through and the event shouldn't occur again.

    You'll either want to call initChallenge within the outer event binding:

    window.addEventListener("load", function() {
        // ...
    
        initChallenge();
    });
    

    Or, you can use an IIFE with the inner binding:

    (function () {
        // ...
    
        window.addEventListener("load", initChallenge);
    })();