I'm attempting to create a function that captures a number of elements on the page, iterates through them and applies a mousedown event listener, then calls another function and passes it some data in a variable. This variable changes throughout the course of the loop, so I want to utilize a closure so that when the event is triggered, the variable has the expected value from that iteration instead of simply referencing the value from the last iteration of the loop.
I have been reading other answers on this site (and others) and have found myself at a loss for how to make this work, because no matter how many of these seemingly correct solutions I use, I continue to get the last value from the data.
Here are a number of variations I've tried - Each shows the expected value when it's attached, but shows the incorrect value when triggered. I'd appreciate your help identifying what I'm doing incorrectly here. Thanks!
Nomenclature Used:
_externalFunction() - The external function that I'd like to call when the event is fired, passing into it the data object.
changingDataValue - The original data value that I'd like sent to the external function.
Using a closure within a different function
function addMouseDown(elem, data) {
elem.addEventListener("mousedown", function () {
_externalFunction(data);
console.log("triggered - mouseDown: " + data.value);
}, false);
console.log("attached - mouseDown: " + data.value);
}
addMouseDown(elements[i], changingDataValue);
Using a closure within another function, and a closure within the event listener
function addMouseDown(elem, data) {
(function(d) {
elem.addEventListener("mousedown", function () {
(function (e) {
_externalFunction(e);
console.log("triggered - mouseDown: " + e.value);
})(d);
}, false);
})(data);
console.log("attached - mouseDown: " + data.value);
}
addMouseDown(elements[i], changingDataValue);
Using a closure within another function:
function addMouseDown(elem, data) {
(function(d) {
elem.addEventListener("mousedown", function () {
_externalFunction(d);
console.log("triggered - mouseDown: " + d.value);
}, false);
})(data);
console.log("attached - mouseDown: " + data.value);
}
addMouseDown(elements[i], changingDataValue);
Closure within the script itself
(function (data) {
elements[i].addEventListener("mousedown", function () {
_externalFunction(data);
console.log("triggered - mouseDown: " + data.value);
}, false);
console.log("attached - mouseDown: " + data.value);
})(changingDataValue);
Closure within event handler
elements[i].addEventListener('mousedown', (function(data) {
return function() {
_externalFunction(data);
console.log("triggered - mouseDown: " + data.value);
};
})(changingDataValue), false);
console.log("attached - mouseDown: " + changingDataValue.value);
Works great except that it actually calls the external function before the event has been triggered, but it does pass in the expected value
(function (elem, data) {
elem.addEventListener("mousedown", (function (d) {
_externalFunction(d);
console.log("triggered - mouseDown: " + d.value);
})(data), false);
console.log("attached - mouseDown: " + data.value);
})(elements[i], changingDataValue);
Again, I appreciate any help anyone can provide here. Thanks!
In your
elem.addEventListener
you're passing a self-executing anonymous function. It is essentially sending the returned value instead of a reference to a function.In other words the following gets executed and the value (in this case,
undefined
) is then passed as the 2nd argument inelem.addEventListener
:If you turn that into a normal function call, your
data
variable will still be in scope when you reference it in the callback (since it is bound to that anonymous function). I'd suggest utilizingcall
/apply
to keep change thethis
scope to the element that was clicked for later development.If I load the code above and then click on link2, then link1 I get the following output: