The letters JS with an arrow flying around

The callbacks are out to get you

03 March 2022

I remember some of the first lines of JavaScript code I looked at were in Jon Duckett’s JavaScript & JQuery. This book and HTML & CSS are the canonical get-into-web-development books. They are truly singular in that they’re the only ever textbooks to actually LOOK GOOD.

Anyway, I remember coming to the section of the book about adding click handlers. There was a code sample that looked something like this:

    let button = document.getElementById("button");
    button.addEventListener("click", onClick);

After this it defined the onClick function:

    function onClick(event){
        console.log("Button has been clicked");
    }

To anyone who knows and loves JavaScript, this is a very simple bit of code.

But it’s also something I remember finding confusing. And since I read that book back in 2019 I had forgotten how confusing it was, until I witnessed some programmers who were new to JavaScript getting caught by it.

My problem was with the arguments to the addEventListener method. This method takes in two arguments. The first is a string describing which event you want to respond to - this makes sense. But what is the second argument?

Well it’s a function. It’s describes what will happen after the button is clicked.

Okay, it’s a function. We write those with round brackets at the end. So I should add the function to the event listener like this, right?

     button.addEventListener("click", onClick());

WRONG! If you write your click handler like this, you’ll click your button and it confusingly won’t do anything. Even though onClick is a function, we have to remove the round brackets after onClick for this line of code.

Why are these round brackets so evil you ask? Every time you’ve seen a function in JavaScript up until now, it’s had a nice pair of round brackets afterward. For instance, when I declare a function:


    // Has some round brackets with no arguments
    function myFunction(){
        console.log("I am such a great function");
    }
    // Has some round brackets with arguments too!
    function add(num1, num2){
        return num1 + num2;
    }

And when I call a function:

    // Has some round brackets when we call them
    myFunction();
    add(3,4);

But all of a sudden we’re doing something that involves functions, but we’re not placing curly brackets after its name? Why?

Functions as a first class citizen #

The reason for what is going on here is that JavaScript treats functions as a first class citizen. Functions are allowed to do anything that any variable can do - get passed into other functions, get pointed to by multiple variables, get mutated. All sorts.

This means this syntax:

    function myFunction(){
        console.log("I am such a great function");
    }

Does pretty much the same thing as this syntax:

    let myFunction = function(){
        console.log("I am such a great function");
    }

Both are valid JavaScript syntax, and both create a variable called myFunction which points to a function. We can pass this variable (which points to a function) INTO ANOTHER function:

    // Declare first function
    function myFunction(){
        console.log("I am such a great function");
    }
    // Declare second function
    function anotherFunction(input){
        input();
    }
    // Here we pass the first function into the second one
    anotherFunction(myFunction);

The big difference when we put the round brackets after a function, is that this means the function will get excecuted immediately when the JavaScript is executed. It’s a way of saying “run this now!”, and if there are arguments “run this now with these arguments”.

When the JavaScript is executing and gets to that line, it will run the function and replace it with its result.

So when we do this:

    button.addEventListener("click", onClick());
    function onClick(event){
        console.log("Button has been clicked");
    }

We’re passing the event listener what our onClick event returns.

And it doesn’t return anything. So, what we’re actually passing into the event listener is undefined.

Instead of passing in undefined, we want to pass our function. That’s why we don’t have a pair of round brackets after the second argument.

It’s a way of saying, “hey, can you do this thing later?”

And that, is a callback.

Back to blog