Callback Functions

A brief clarifying look into how callback functions are used.

Scroll down...

Content

Resources

Comments

We've thrown around "callback functions" (aka "handlers") a lot in the past couple of lessons but it's worth pausing for a moment and making sure you have a strong understanding of exactly what these functions are and how to use them. Specifically, the difference between immediately executing a callback function, wrapping it in an anonymous function, or passing in the function definition are all typically confusing for beginners. In this brief lesson, we'll clear that up.

The Point of Callbacks

Callback functions are just JavaScript functions that you intend to execute at a future date when some action has been completed or some event triggers them to run. Though this pattern is used frequently in jQuery, it's present throughout JavaScript and is critical to understand well. Luckily, it's also quite straightforward.

Callbacks Are Function Definitions

As you know, there is a big difference between passing a function definition around and actually calling that function using parentheses. When you pass a function as a callback, you need to pass its definition (which is often saved to a variable):

// Define a generic function to be our callback
var yourCallback = function(){ console.log("Hello?") }


// Define a generic function that takes a
//   callback for execution at some later date
var myDelayedFunction = function( yourCallback ){
  yourCallback();
}


// Passing in the function definition as our
//   callback, which is good!
myDelayedFunction( yourCallback );


// Oops! This executes the callback immediately
//   by accident when we pass it in! BAD!
myDelayedFunction(yourCallback())

Arguments to Callbacks

What about callback functions that take arguments? There's actually no difference... just pass in the callback function definition like before and assume that the calling function will be smart enough to pass in the correct parameters:

// Define a callback to pass back the
//   original target of our click
var whoGotClicked = function( event ) {
  console.log( event.target );
}


// the `click` method passes in the click
//   Event to its handler (callback) function
$("a").click( whoGotClicked )


// Now, when we click our anchor tag, it will
//   display the actual DOM element that was
//   clicked.

It's that simple.

If you're wondering what might be going on behind the scenes, the function which runs your callback probably has a structure sort of like this:

var click = function( callbackFunc ) { 
  // ...code to listen for clicks
  // and set the `event` variable to
  // event object which all events use...

  // Run the callback function and pass in
  // the `event` variable to it
  callbackFunc( event )
}

Anonymous Wrapper Functions

Sometimes you just don't want to go through the effort of first defining a function, then naming it, then passing that named function in as a handler/callback. That seems like a lot of work if you're only planning on using your callback function once. Why not just define the callback function right there in the arguments for the other function?

This is a very common pattern:

// define an anonymous function right there
//   in the arguments for our click listener
$("a").click( function( eventArg ) {
  console.log( eventArg.target )
  alert( "SOMEONE GOT CLICKED!" )
});

The key is realizing that, just like before, we aren't actually running the anonymous function (yet). The parentheses often throw people off -- those parentheses do NOT mean we are running the anonymous function right now because they are part of the definition of that function.

The return value of function (specifically, function( eventArg ){...} above) is the definition of that anonymous function, so passing it into our click helper as its handler accomplishes exactly the same thing as if we'd passed in the definition of a named function like you saw before.

It is quite common to wrap existing functions in anonymous functions in callbacks as well because it gives you more control over exactly what to pass into those functions. Here's a made-up example:

// Define a random function to tell us what happened
var tellMeWhatHappened = function( whatHappened ) {
  console.log( whatHappened )
}


// Run our new function inside of the anonymous
//   function so we can pass it whatever inputs
//   we choose instead of the boring ones that
//   get passed into the callback.
$("a").on("click", function(event) {
  tellMeWhatHappened( "We clicked on " + event.target + "!" );
});

You may find yourself asking "how do I run this function as a callback when it takes all sorts of special inputs?"

...Now you know how: wrap it in an anonymous function.

Be Careful of 'This'

One quick note: The value of this will change depending on which context you execute something in. As an example, let's look at this using a similar example as above:

// Define a random function to give us 'this'
var whatIsThis = function() {
  console.log( "In our named function, 'this' is now: " )
  console.log( this )
}


// Run our new function inside of the anonymous
//   function so we can pass it whatever inputs
//   we choose instead of the boring ones that
//   get passed into the callback.
$("a").on("click", function(event) {
  console.log( "In our callback, 'this' is: ")
  console.log( this )
  whatIsThis();
});

Now you can see that, if we click on an anchor tag, it will output two different "this" properties, depending on which function is being run:

// In our callback, 'this' is:
// <a href="http://www.example.com">Click me!</a>
// In our named function, 'this' is now:
// Window {top: Window, location: Location, document: document, window: Window, external: Object…}

So:

  1. this is the target of the event inside the callback function (our anonymous function)
  2. this changes to the global window object on our named function because we haven't called it method-style on any object and we haven't supplied it with a this of its own.

Be careful of that second part! The moral of the story: this is only the targeted DOM element inside the callback, NOT inside any other functions it calls.

Code Review

The important bits of code from this lesson

// GOOD -- pass in a function definition
$("a").click( someFunc )

// BAD -- accidentally call that function
$("a").click( someFunc(arg1) )

// GOOD -- Define an anonymous function when
//   setting up the listener
$("a").click( function( arg1 ){...} )

// GOOD -- Run a named function inside your
//   anonymous callback
$("a").click( function(){
  someOtherFunc( arg1, arg2 )
})

Wrapping Up

Callbacks are a straightforward concept but one which will probably cause you to question your sanity more than a few times over the coming units. Just remember -- functions which take callbacks are asking for a function definition which will be run at some later date.

Because callbacks are executed at some future point in time, truly understanding them requires you to fully appreciate the concepts of scope and closures, which we'll cover in a future section. For now, though, you know everything you need to use them effectively in the upcoming projects.



Sign up to track your progress for free

There are ( ) additional resources for this lesson. Check them out!

There are no additional resources for this lesson just yet!

Sorry, comments aren't active just yet!