Foliotek Development Blog

Keep Original Variable State Between Event Binding and Execution – JavaScript

by Brian Grinstead

Also known as: Binding Events Inside of a Loop with jQuery

Sometimes I want to create a closure that accesses a variable in the state that it was in when the closure was created. I don’t want to know what the current value is, I want to know what the value was when I created the function. I may want to do this when binding an event to a DOM object, or when executing a function inside of setTimeout().

The Problem

Here is an example of the problem using the jQuery library (consider what will happen when you click on any of the li elements):


	$(function() {
		$("body").append("<ul id="container"></ul>");
		
		for (var i = 0; i < 5;="" i++)="" {="" var="" $item=""><li></li>").text("Item " + i);
			$("#container").append($item);
			
			$item.click(function() {
				alert("You clicked number " + i);  // always "You clicked number 5"
			});
		}
	});

The event handler will always use the final value of the variable “i”, which will be 5 in this case. One way to fix this problem is to create a function and pass “i” as a formal parameter:

Solution 1 – Traditional Function


	function bindItem($item, ind) {
		$item.click(function() {
			alert("You clicked number " + ind);  // Works as expected
		});
	}

	$(function() {
		$("body").append("<ul id="container"></ul>");

		for (var i = 0; i < 5;="" i++)="" {="" var="" $item=""><li></li>").text("Item " + i);
			$("#container").append($item);
			bindItem($item, i);
		}
	});

Since this passes the variable as a parameter to a function, the variable “ind” will always remain the same, even if “i” changes after the function finishes execution. The function bindItem() creates a new scope. Any variables (passed as parameters or declared inside the function) will reserve their own space in memory, and will not be affected by the state of the variable ‘i’. Thanks to reddit user Mych for correcting me.

This is what I wanted in the first place, but we may not want to have a named function here – if we used a closure we would still have access to variables in the same scope (like $item) without having to pass them as parameters to bindItem().

This does the trick:

Solution 2 – Self Executing Function


	$(function() {
		$("body").append("<ul id="container"></ul>");
		
		for (var i = 0; i < 5;="" i++)="" {="" var="" $item=""><li></li>").text("Item " + i);
			$("#container").append($item);
			
			(function() { // Closure here here instead of "bindItem()"
				var ind = i;
				$item.click(function() {
					alert("You clicked number " + ind); // Works as expected
				});
			})(); // Execute immediately
		}
	});

This is a trick I first saw in the jQuery plugin development tutorial, in the custom alias section.

Declaring the closure creates a new scope in the loop. Any variables (such as ‘ind’) passed as parameters or declared inside of this scope will reserve a new space in memory that is not a reference to ‘i’.

The extra parentheses at the end of a block like this “(function() {…})()” cause the closure to execute immediately .

These are just a couple ways to solve the problem of variable state changing between event binding and execution. Closures are a powerful language feature that makes JavaScript very flexible, but when execution of them is being delayed via a timeout or event handler,it is important to keep track of the variables that they are closed over.

Tags: , , , , , ,

« »

One Response to “Keep Original Variable State Between Event Binding and Execution – JavaScript”

  1. Brian Grinstead » Blog Archive » Keep Original Variable State Between Event Binding and Execution Says:

    [...] wrote an article over on the LANIT Development Blog about saving the state of a variable inside a closure that is [...]

Leave a Reply