I am new to JavaScript, I've done most of my coding in Python, and haven't found a clear answer to this question.
If I have an object containing element ids and function parameters, and I want to add event handlers (pointing to the same function) to the ids inside the object - how do I do it?
I've been looping over them using a for loop and adding "click" listeners on them, but as I loop through, the function is executing instead of just putting the event listener on.
I see this as my next step in writing more efficient JavaScript and would love some pointers.
var delay2 = 1000; //smooth scroll time
scroll_list = {
"review":"book_apt",
"review2":"book_apt",
"home_nav":"home",
"what_wedo_nav":"what_we_do",
"save_early_nav":"save_early",
"reviews_nav":"reviews",
"our_team_nav":"our_team"
}
// loops through scroll list.
for (key in scroll_list){
let id = document.getElementById(key); //gets id from object
id.addEventListener("click", navigate(scroll_list[key]), false); //adds event listener and passes the parameter from the object to the function.
}
// this scrolls to a certain id onclick
function navigate(loc){
$('html, body').animate({
scrollTop: $("#"+loc).offset().top
}, delay2);
}
1 Answer 1
The root cause
The following line within your for loop directly invokes the function as you're using the navigate(scroll_list[key])
as the second parameter:
id.addEventListener("click", navigate(scroll_list[key]), false)
The expression says 'call the navigate
function right now'.
Setting a reference to the function without calling it
To use a function as an event handler, you need to pass it as a reference (i.e. navigate
instead of navigate()
):
id.addEventListener("click", navigate, false)
This doesn't directly call the function but only registers it to be called (when element with corresponding id is clicked).
Setting a reference to the function including parameters
If you want to also pass different parameter values (e.g. for each id), you can wrap the target function in an anonymous function:
id.addEventListener("click", (function () {
const loc = scroll_list[key];
return function () {
navigate(loc)
}
})(), false);
Again, this doesn't call the function. It registers it to be called with the desired parameter (the respective id in your case) when the element with corresponding id is clicked.
You've surely noticed that the anonymous function inserted in this step is more complex than the one in the previous option. It's needed to address a specificity in JavaScript.
Variables in JavaScript are scoped to functions, not blocks of code. Hence, to capture the 'current' value of scroll_list[key]
at each iteration, we need to store it in a new variable loc
wrapped within another anonymous function. Otherwise, the value of scroll_list[key]
for any further use (i.e. for use with all event listeners) would stay at the value obtained at last iteration.
addEventListener
you're calling the navigate function:navigate(scroll_list[key])
. The event handler has the signatureevent -> void
(see this); you need to wrap it in a lambda:(event) => navigate(scroll_list[key])
(in Python it would be:lambda event: navigate(scroll_list[key])
).