How to add eventListeners within a loop in JavaScript without triggering JSHint’s confusing semantics error?

I am working on creating a dynamic list of items in JavaScipt using DOM. Each item has a link (an HTML element) that links to another element on the page; a form that allows the user to edit the team. The link works fine, but I’m attempting to make the form work by adding an eventListener to the link, since the form needs to get the team that we are editing as a reference.

Here’s the code in question:

    for (let i = 0; i < teams.length; i++) {
    let row = table.insertRow(-1);
    //removed code that made cells under the row
    let link = document.createElement("a");
    link.setAttribute("href","#form");
    link.team = teams[i];
    link.addEventListener("click", function (e) {
        updateForm(link.team);
    });
    link.textContent = teams[i].name;
    //code that adds the link and other elements into the cells of the table
}

I have removed some parts of the code for clarity. The important thing is the addEventListener, which functions perfectly. The way it is right now currently correctly sends the team in question when the user clicks on the button. However, I am getting the following message from JSHint:

(error) Functions declared within loops referencing an outer scoped variable may lead to confusing semantics.

If I’ve understood correctly, this is because the variable tableexists outside of the scope of the loop. However, I do not understand why getting the table inside of the loop would change anything. What exactly am I supposed to change in the code to make sure that JSHint would be happy? I can also use some other method than adding an eventListener if it seems like a better solution.

Cheers!

Answer

If I’ve understood correctly, this is because the variable tableexists outside of the scope of the loop.

link, not table.

What exactly am I supposed to change in the code to make sure that JSHint would be happy?

I’d come at it from the other side: What do you need to change in JSHint / your linting setup so that your perfectly-valid code using link in a callback doesn’t trigger a linting error? If your code were using var the warning would be very useful, but your code is using let.

IIRC, JSHint is much more configurable than the JSLint it was originally based on (years ago now), you might look to see if there’s an option that turns off this particular warning when you’re using let for link.

Alternatively, you might look into another linting solution (ESLint is quite popular).

But there is the small chance that you might change the team property of the object that link refers to. If you want to eliminate that possibility and get rid of the explicit function, you could use bind:

link.addEventListener("click", updateForm.bind(null, link.team));

That will also get rid of the JSHint warning.

Or since you’re using an expando property on the element itself, you could use this:

link.addEventListener("click", function() {
    updateForm(this.team);
});

…but I’d suggest you not use expando properties on DOM elements. For instance:

for (const team of teams) {
    const row = table.insertRow(-1);
    //removed code that made cells under the row
    const link = document.createElement("a");
    link.setAttribute("href","#form");
    link.addEventListener("click", () => { // Or:
        updateForm(team);                  //     link.addEventListener("click", updateForm.bind(null, team));
    });                                    // 
    link.textContent = team.name;
    //code that adds the link and other elements into the cells of the table
}

Leave a Reply

Your email address will not be published. Required fields are marked *