Skip to main content
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Vicky Ryder
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Vicky Ryder

Wrapping The Window Object In A jQuery Wrapper

By
Published in Comments (6)

This morning, when I was building my Flickr-style photo tagging demo using jQuery, I got totally stumped trying to debug a variable problem. From what it looked like, the Window object appeared to be undefined within the context of my document-ready event handler. After picking apart the code and commenting out bits of it for like 10-15 minutes, I finally figured out what the heck was going on. It boiled down to me simply forgetting how variables get "compiled" in Javascript. I think it's something that's very easy to forget, so I figured I would share my error.

Let's walk through a really simple scenario. In the following demo, I try to create a function-local jQuery-ized variable to override the raw window object reference:

<!DOCTYPE HTML>
<html>
<head>
	<title>Wrapping The Window In jQuery</title>
	<script type="text/javascript" src="jquery-1.4.1.js"></script>
	<script type="text/javascript">

		// When the DOM is ready to be interacted with, initialize
		// the scripts.
		jQuery(function( $ ){

			// Overwrite the local window reference by create a
			// local jQuery "window" wrapper for access to things
			// like width() and scrollTop().
			var window = $( window );

			// Log window.
			console.log( window );

		});

	</script>
</head>
<body>

	<h1>
		Wrapping The Window In jQuery
	</h1>

</body>
</html>

As you can see, I am creating a local variable, window, which is a jQuery wrapper containing the global window object. The intent here was to create a locally-scoped window variable that would give me immediate access to jQuery methods like width(), height(), scrollLeft(), and scrollTop(). At first glance, this seems reasonable; but, when we run the above code, this the console output that we get:

[ ]

It's a jQuery wrapper alright, but the collection is empty.

When I saw this, my first thought was that the global window object was undefined in the context of the document-ready event handler. To test this, I tried putting a console log() method call before and after the variable assignment:

<!DOCTYPE HTML>
<html>
<head>
	<title>Wrapping The Window In jQuery</title>
	<script type="text/javascript" src="jquery-1.4.1.js"></script>
	<script type="text/javascript">

		// When the DOM is ready to be interacted with, initialize
		// the scripts.
		jQuery(function( $ ){

			// Log the window.
			console.log( "Before: ", window );

			// Overwrite the local window reference by create a
			// local jQuery "window" wrapper for access to things
			// like width() and scrollTop().
			var window = $( window );

			// Log window.
			console.log( "After: ", window );

		});

	</script>
</head>
<body>

	<h1>
		Wrapping The Window In jQuery
	</h1>

</body>
</html>

Here, you can see that I am logging the global window object, then creating the local window reference (using the window object), then logging the newly created window reference. When we run the above code, we get the following console output:

Before: undefined
After: [ ]

Ah-ha!! The window object is undefined in the context of the document-ready event handler! It doesn't even exist before I've tried to do anything with it. When I saw this, I figured something really crazy must be going on.

After staring at this code for like 5 minutes, I had another "ah-ha" moment. What I realized was that the above behavior was exactly what was to be expected. The problem was that I had become so accustomed to thinking about Javascript in a top-down manner, that I forgot that it actually does "compile", at least in a very lose sense, for optimization. In particular, one thing it does is move all of your variable declarations to the top of the current function. To give you a basic idea of what I'm talking about, here is my interpretation of the "compiled" version of the previous code sample:

<!DOCTYPE HTML>
<html>
<head>
	<title>Wrapping The Window In jQuery</title>
	<script type="text/javascript" src="jquery-1.4.1.js"></script>
	<script type="text/javascript">

		// When the DOM is ready to be interacted with, initialize
		// the scripts.
		jQuery(function( $ ){
			// -------------------------------------------- //
			// -------------------------------------------- //

			var window;

			// -------------------------------------------- //
			// -------------------------------------------- //

			// Log the window.
			console.log( "Before: ", window );

			// Overwrite the local window reference by create a
			// local jQuery "window" wrapper for access to things
			// like width() and scrollTop().
			window = $( window );

			// Log window.
			console.log( "After: ", window );

		});

	</script>
</head>
<body>

	<h1>
		Wrapping The Window In jQuery
	</h1>

</body>
</html>

As you can see here, the Javascript interpreter (Me) has moved the "var" Window variable declaration up to top of the function and removed the "var" keyword form the actual variable assignment. The Javascript interpretor does this to allow our var usage to be easily sprinkled throughout our code without complicating scope-chain-crawl. Once it does this, however, you can quickly see that my local window reference is going to try to reference itself, rather than the global window object, in some sort of ill-fated mobius strip approach.

So now that we see why this is breaking, what can we do to create a local, jQuery-ized window object? The simplest way would be to create a reference to the window object outside of the document-ready event handler:

<!DOCTYPE HTML>
<html>
<head>
	<title>Wrapping The Window In jQuery</title>
	<script type="text/javascript" src="jquery-1.4.1.js"></script>
	<script type="text/javascript">

		// Create a window-reference outside of the event handler.
		// The event handler will create a closure to this scope,
		// which will maintain this variable reference.
		var thisWindow = window;

		// -------------------------------------------------- //
		// -------------------------------------------------- //

		// When the DOM is ready to be interacted with, initialize
		// the scripts.
		jQuery(function( $ ){
			// Overwrite the window object, using the thisWindow
			// proxy variable.
			var window = $( thisWindow );

			// Log window.
			console.log( "After: ", window );
		});

	</script>
</head>
<body>

	<h1>
		Wrapping The Window In jQuery
	</h1>

</body>
</html>

As you can see here, we've created a variable, thisWindow, which points to the global window object. When we then create the document-ready event binding, we pass an anonymous function off to jQuery. This anonymous function creates a closure to the global scope, granting the event handler access to the thisWindow variable during its execution. Now, since the variable assignment doesn't rely on a reference to "window", the locally-scoped window variable has no problems wrapping the thisWindow reference in a jQuery collection. And, when we run the above code, we get the following output:

After: [ Window window_in_ready.htm ]

Another solution to this problem would be to leverage the power of self-executing functions. In a self-executing function situation, we can create locally-scoped variables through input-argument translation. To see what I mean, take a look at this code:

<!DOCTYPE HTML>
<html>
<head>
	<title>Wrapping The Window In jQuery</title>
	<script type="text/javascript" src="jquery-1.4.1.js"></script>
	<script type="text/javascript">

		// Create a self-executing function. The parameters to
		// this function - window, $ - will create locally-scoped
		// versions of the arguments being passed in.
		(function( window, $ ){


			// When the DOM is ready ......
			$(function(){

				// Log window.
				console.log( "Local: ", window );

			});


		})(
			jQuery( window ),
			jQuery
		);

	</script>
</head>
<body>

	<h1>
		Wrapping The Window In jQuery
	</h1>

</body>
</html>

Here, we define the self-executing function's parameters as "window" and "$". These parameters then become our locally-scoped versions of whatever is being passed in during the self-executing method invocation. In our code, the following variable translation is taking place during said invocation:

jQuery( window ) .... becomes .... window

jQuery .... becomes .... $

As you can see, when we invoke the method, we use the global window object to create a nameless jQuery collection. This jQuery collection then gets assigned to the locally-scoped "window" parameter. I happen to really like this approach as it creates a very sexy and elegant encapsulation.

So much of Javascript is written in a top-down manner that it's very easy to forget that the code does get "compiled" to some degree (this may be even more true with the most recent browsers). When you're trying to do something tricky, like create a local reference of a globally available object, forgetting this fact can cause some serious head banging and hair pulling!

NOTE: I use the term "compiled" in the loosest possible manner.

Want to use code from this post? Check out the license.

Reader Comments

4 Comments

Very elegant solution... and good reminder. It's always the little stuff that leaves you wanting to throw your monitor through the window !

10 Comments

I use the self executing lambda with params a LOT, but with window using self might be the simplest:

jQuery( function( $ ){ var window = $( self ); console.log( window ); } );

15,848 Comments

@Jim,

Oh wow, I forgot that window had a "self" property (it's been forever since I cared about framesets). Dynamite drop-in.

1 Comments

Nice solution, but you could simply use other identifier than 'window' for the wrapped window object and the problem would be solved. Since javascript has function scope, the local variable window overrides the global window object as soon as the function get executed.

jQuery(function($) {
console.log('before: %o', window);
var wrappedWindow = $(window);
console.log('after: %o', wrappedWindow);
});

15,848 Comments

@Elvis,

Yes, very true; I just liked the idea of using the "window" name. Since it's used so often, it just felt natural. But again, that's just a personal preference issue - your approach is good as well.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel