Search code examples
javascriptecmascript-5

How to get a variable that works the same way as one declared with "let" or in a with block?


I'm not sure what to call this arrangement but heres a common problem - if I do this:

var buttons = document.querySelectorAll(".button"); //select a bunch of buttons
for (var i = 0; i < buttons.length; i++) {
    buttons[i].addEventListener("mouseup", function(){
        alert(buttons[i].getAttribute("data-message"));
    }); 
}

which is creating a bunch of anonymous functions, one for each button to display the message in the buttons attribute, buttons[i] has to be captured in the loop but will turn up as "undefined"

the 2 ways I know around this are 1: let statement

var buttons = document.querySelectorAll(".button"); //select a bunch of buttons
for (let i = 0; i < buttons.length; i++) {
    buttons[i].addEventListener("mouseup", function(){
        alert(buttons[i].getAttribute("data-message"));
    }); 
}

works just fine, as well as 2: with statement

var buttons = document.querySelectorAll(".button"); //select a bunch of buttons
for (var i = 0; i < buttons.length; i++) {
    with ({"button":buttons[i]}) {
        button.addEventListener("mouseup", function(){
            alert(button.getAttribute("data-message")); 
        }); 
    }
}

works fine. Problem is though, I'm faced with an employer that is on the next friggin level with his expectations on compatibility so "let" is out since its technically still not standard and "with" is also out since its being deprecated.

That leaves me with no clue on how to deal with such situations in a nice way, is there a third option that is standard and won't be deprecated?


Solution

  • if you need to use es5 code without compiling down from es6, theres two easy ways to go about it: convert the buttons variable to an array and iterate over it with a forEach or create a small closure that you call inside your for loop:

    option 1:

    var buttons = document.querySelectorAll(".button"); //select a bunch of buttons
    Array.prototype.forEach.call(buttons, function(el) {
        el.addEventListener("mouseup", function(){
            alert(el.getAttribute("data-message"));
        }); 
    }
    

    or option 2:

    var buttons = document.querySelectorAll(".button"); //select a bunch of buttons
    function addButtonListener(j){
        buttons[j].addEventListener("mouseup", function(){
            alert(buttons[j].getAttribute("data-message"));
        }); 
    }
    
    for (var i = 0; i < buttons.length; i++) {
        addButtonListener(i);
    }
    

    this closure must be called after buttons has been set