FAQ: DOM Events with JavaScript - Adding Event Handlers

This community-built FAQ covers the “Adding Event Handlers” exercise from the lesson “DOM Events with JavaScript”.

Paths and Courses
This exercise can be found in the following Codecademy content:

Web Development

FAQs on the exercise Adding Event Handlers

There are currently no frequently asked questions associated with this exercise – that’s where you come in! You can contribute to this section by offering your own questions, answers, or clarifications on this exercise. Ask or answer a question by clicking reply (reply) below.

If you’ve had an “aha” moment about the concepts, formatting, syntax, or anything else with this exercise, consider sharing those insights! Teaching others and answering their questions is one of the best ways to learn and stay sharp.

Join the Discussion. Help a fellow learner on their journey.

Ask or answer a question about this exercise by clicking reply (reply) below!

Agree with a comment or answer? Like (like) to up-vote the contribution!

Need broader help or resources? Head here.

Looking for motivation to keep learning? Join our wider discussions.

Learn more about how to use this guide.

Found a bug? Report it!

Have a question about your account or billing? Reach out to our customer support team!

None of the above? Find out where to ask other questions here!

Perhaps the author is referring to something like the following:

object.onclick = e => console.log(e.target)

The expression on the right is an anonymous function. We can write it that way, or we can give in a name and write it on the side, then use that name as a callback.

const log_target = e => console.log(e.target);

object.onclick = log_target;

Function expressions are called such since they are anonymous and either immediately executed, or assigned to a variable for later or repeated execution. Above we saw the use case for both.

Let’s examine one of JS’s iterators, Array.forEach(). This function takes a an anonymous function as an argument.

Array.forEach(function(x) {
    console.log(x);
});

We can write the function as a callback, and use it instead…

const log_x = x => console.log(x);

Array.forEach(log_x);

For each element in the array, the iterator will call the function on that element.

Great response :grinning:. I realized what the author meant after doing more exercises - that’s why I removed my post (apparently while you were replying). Perhaps the lesson should be reworded to something like this: “It’s best practice to pre-define a callback function, instead of declaring an anonymous functions as part of the event handler property” since anonymous function does not necessarily mean unnamed.

At this stage almost any manner of explanation is bound to be ambiguous for some, even confusing. There is no guarantee that revising the lesson text will have more or less ambiguity. No matter, we should expect to stumble a little until we get our bearings. Bear with it, and use the forums if you are unclear.

1 Like

Happy New Year, Roy!

I have a new question, but it is so closely related to this thread, I’m replying to this previously “closed-out” topic (I hope that’s consistent with the community guidelines - I’m still new). Here goes:

I noticed throughout this lesson that when we assign event handler functions to event properties, we omit the parenthesis when assigning a callback function. It wasn’t until I incorporated the learnings from this lesson into a personal project that I realized the parenthesis are omitted because we are not calling the function at the time we assign it to the event property… we are assigning the function to the property (which up till that point is empty - mind blown). I ran into problems when my callback function actually needed a parameter. My non-functional code looked something like this:

const doThis = function(index) {
  // something you don't need to see
  things[index].style.display = 'none';
  otherThings[index].style.display = 'block';
  //other things you don't need to see lol
}

for (let i=0;i<things.length;i++) {
  things[i].onclick = doThis(i);
}

Then the aha moment above hit me and I realized I needed to nest the contents of the callback function within a parameterless anonymous function:

for (let i=0;i<things.length;i++) {
  things[i].onclick = function() {
    // something you don't need to see
   things[i].style.display = 'none';
   otherThings[i].style.display = 'block';
   //other things you don't need to see lol
  }  
}

The above code functions exactly as I want it. Another approach occurred to me as I’m writing this post - I could just nest a call to doThis() within the anonymous function. My question was going to be how do I use a named callback function that takes a parameter, but I think I just answered it with the nested call. Do I grok it? Is there another approach that is more typical?

Thanks!

Try this out for size…

things.forEach(x => x.onclick = doThis);

That can take the place of your for loop. forEach() is a JS iterator covered in the unit on iterators which should have been covered by now.

I am familiar with forEach, but I don’t see how it could work here - not that your example wouldn’t work for some applications, but where do we pass in the parameter to doThis(index)? Read my post again. I will try to add more depth:

I have two arrays of equal length, each with values paired to the other by index (things[0] has a relationship with otherThings[0]). I want to associate a function for each click of the elements that belong to things that makes changes to those things elements and the corresponding elements of otherThings. Your example (unless I am mistaken and I am sorry if that is the case) would only work if doThis() takes in no parameters (e.g. modifies each index of things but only one index of otherThings). If I am wrong about your example, clearly my understanding of the syntax is incomplete. If so, please help me understand. Otherwise, as I said before, what I have works well (I did try nesting the function call per my previous post and that worked too!). I was only curious if there were other (perhaps more accepted) methods of doing what I did. Thanks again.

The only parameter we need to supply the event handler is e or event which will be bound by the EventObject. What we call it is arbitrary since it is a internal variable.

We can identify the index of the event target (e.target), then apply it to another array.

1 Like

Okay cool. Got this to work. My code below:

 const doThis = function() {
   let index = things.findIndex(e => e === event.target);
   event.target.style.display = 'none';
   otherThings[index].style.display = 'block';
 }

things.forEach(elem => elem.onclick = doThis);

Compare this to my other solution:

const doThis = function(index) {
  things[index].style.display = 'none';
  otherThings[index].style.display = 'block';
}

for (let i=0;i<things.length;i++){
  things[i].onclick = function() {
     doThis(i);
  }
}

To select the best option, it looks to me like an optimization exercise. Although forEach() and findIndex() are built-in methods, they are still at their core loops. Even though the first solution does not require the developer to write a loop, it has nested loops - each iteration of the forEach() method must cycle through findIndex() iterations until its condition is satisfied. In contrast, the for loop solution is “single-axis.” Therefore I expect the for loop to be more efficient with machine time. Is this logic sound?

At the Adding Event Handlers exercise- see below, there is this code snippet :

view.onclick = open;
close.onclick = hide;

I 'm wondering what open and hide is and how it got set as a value.
Thanks
Susie

// Add the code you want to test below:
let view = document.getElementById('view-button');
let close = document.getElementById('close-button');
let margo = document.getElementById('margo');

let open = function() {
  margo.style.display = 'block';
  close.style.display = 'block';
};

let hide = function() {
  margo.style.display = 'none';
  close.style.display = 'none';
};

view.onclick = open;
close.onclick = hide;

// Write your code here

let textChange = function() {  
view.innerHTML = 'Hello, World!';
}

let textReturn = function() {
  view.innerHTML = 'View!';
}

view.addEventListener('click', textChange);
close.addEventListener('click', textReturn);

The variables open and hide are functions defined directly above the statements:

view.onclick = open;
close.onclick = hide;

namely,

let open = function() {
margo.style.display = ‘block’;
close.style.display = ‘block’;
};

let hide = function() {
margo.style.display = ‘none’;
close.style.display = ‘none’;
};

We can see in the code that they are functions. When we assign them to a listener we do not invoke them, though, just pass a reference. When the event is fired, the function will then be invoked to make the changes in the DOM.

1 Like

Has anybody else encountered this weird bug in the exercise.
before even starting the exercise it throws up the error Did you correctly create the textReturn function? and marks task 1 as failed.

Ignoring this and following the instruction for task 1, it then gives you the misspelled error Did you chang to ‘Testy’

Pushing on, and completing the tasks to completion despite this doesn’t complete the lesson (something that has worked with other buggy lessons in the past). Thinking I must have messed up, and getting the solution gives the same code with some minor formatting differences. deleting this code and pasting my own code in after this lets my code pass.

I got the same error also.

I typed in ‘Testy’ instead of ‘Hello, World!’ and that let me pass the task 1.
Same thing for task 2. ‘Testy’ instead of ‘View’.

After doing the rest of the tasks as is, I went back and corrected the ‘Testy’ to the correct strings.

I don’t know why this happens.

Hi, I would like to ask how to reference a function.

In the excersice “Adding event handlers”,
I wrote the addEventListener as:

view.addEventListener(‘click’, textChange())

which resulted in the event beeing active all the time (starting on ‘Hello World’ not ‘View’).
Adding the:

close.addEventListener(‘click’, textReturn());

which resulted in the textReturn beeing active all the time (always on ‘View’ not changing to ‘Hello, World’).
However removing the () next to the function solve the issue. So i would like to ask, if in listeners there is some special way how to reference function without parameters, as this never resulted in an issue before.

When the () (parens) are included, the function is invoked. When they are not included, the function is passed as a reference only, and not invoked until the actual event occurs. This is what we refer to as a callback.

2 Likes