Functions declared within loops referencing an outer scoped variable
may lead to confusing semantics. (container, image)
Simply a warning, so doesn't break code, but points to a way to avoid the warning—pass the variables through function parameters. This is not directly possible with the flagged function here: ...addEventListener("click", function()...
, because it's an event handler and it utilizes predetermined parameters—so wrap the addEventListener
in an anonymous, immediately invoked function expression (IIFE), and pass the offending variables as parameters. This creates a function scope that both separates the outer variables from the inner scope and still makes them available to the inner scope.
Replace:
image.addEventListener("load", function() {
container.appendChild(image);
});
...with:
(function (container, image) { // <-- instantiate function variables
image.addEventListener("load", function() {
container.appendChild(image);
});
}(container, image)); // <-- pass the outer variables to the function
There are two of the same warnings presented. The same method can be used, or...
Another method to pass additional variables to an event handler is to write a function that returns a function. Passing the variables to the outer function creates a function scope that, again, both separates the outer variables from the inner scope and still makes them available to the inner scope. To pass the return
'ed function to the event execute the outer function in place, i.e., in the addEventListener()
parameters where the handler function normally goes:
Replace:
container.querySelector("button").addEventListener("click", function() {
let iframe = document.createElement("iframe");
iframe.setAttribute("frameborder", "0");
iframe.setAttribute("allowfullscreen", "");
iframe.setAttribute("allow", "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture");
// Important: add the autoplay GET parameter, otherwise the user would need to click over the YouTube video again to play it
iframe.setAttribute("src", "https://www.youtube.com/embed/" + container.dataset.videoId + "?rel=0&showinfo=0&autoplay=1&fs=0");
// Clear Thumbnail and load the YouTube iframe
container.innerHTML = "";
container.appendChild(iframe);
});
} // end of YouTubeContainers for loop
...with:
container.querySelector("button")
.addEventListener(
"click",
containerBtnHandler(document, container) // <-- execute the function that returns the handler function
// ...and pass the outer variables to it
);
} // end of YouTubeContainers for loop
// PUT the function that returns the handler OUTSIDE the for loop...
function containerBtnHandler (document, container) {
return function () { // return the original event handler
let iframe = document.createElement("iframe");
iframe.setAttribute("frameborder", "0");
iframe.setAttribute("allowfullscreen", "");
iframe.setAttribute("allow", "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture");
// Important: add the autoplay GET parameter, otherwise the user would need to click over the YouTube video again to play it
iframe.setAttribute("src", "https://www.youtube.com/embed/" + container.dataset.videoId + "?rel=0&showinfo=0&autoplay=1&fs=0");
// Clear Thumbnail and load the YouTube iframe
container.innerHTML = "";
container.appendChild(iframe);
};
}
Warning: This only avoids the warning, but doesn't necessarily remove the possibility of "lead[ing] to confusing semantics." Mastery of a craft means one knows when and how they can break the "rules" with impunity.
Here's a SO discussion on the topic of the warning: Functions declared within loops referencing an outer scoped variable may lead to confusing semantics.