Using Self-Executing Function Arguments To Override Core jQuery Methods
Overriding jQuery methods (or any function for that matter) is a relatively straight forward process. All you have to do is get a reference to the old method (if you need to keep it) and then override the object key (method name) using your new function. A while back, however, I was reading Eric Hynds' blog and came across what I thought was a really elegant way to do this. He was using self-executing function arguments to create a reference to the old jQuery method that he was about to override.
When you create a self-executing function block, you have the opportunity to pass in parameters as part of the function invocation:
(function( argA, argB ){ .... })( paramA, paramB );
In this case, we are passing in "paramA" and "paramB", which will become the arguments, "argA" and "argB," respectively. This approach has a number of benefits including cutting down on scope-chain traversal and creating reference short-hands (ex. mapping "jQuery" parameter to "$" argument). What Eric was doing, that I found so interesting, was that he was using this parameter-argument approach to create references to methods that he was going to override.
To see this in action, I created a very brief demo that overrides the core jQuery method, html(). In the following code, I am going to prepend my own string data to any given HTML before I pass it off to the original, now wrapped, html() method:
<!DOCTYPE html>
<html>
<head>
<title>Overriding Core jQuery Methods</title>
<script type="text/javascript" src="./jquery-1.4.2.js"></script>
<script type="text/javascript">
// Create a self-executing function block to which we are
// going to pass the jQuery object and a reference to the
// original, core method, html().
(function( $, oldHtmlMethod ){
// Override the core html method in the jQuery object.
$.fn.html = function(){
// Check to see if we have an argument (that the user
// is setting HTML content).
//
// NOTE: This does not take into account the fact
// that we can now pass a function to this method -
// this is just a lightweight demo.
if (arguments.length){
// Prepend our own custom HTML.
arguments[ 0 ] = (
"<strong>HTML: </strong>" +
arguments[ 0 ]
);
}
// Execute the original HTML method using the
// augmented arguments collection.
return(
oldHtmlMethod.apply( this, arguments )
);
};
})( jQuery, jQuery.fn.html );
// -------------------------------------------------- //
// -------------------------------------------------- //
// When the DOM is ready, run scripts.
$(function(){
// Use the HTML method which, at this point, has been
// overridden with a wrapper behavior.
$( "body" ).html( "I was appended to BODY!" );
});
</script>
</head>
<body>
<!-- Intentionally left blank. -->
</body>
</html>
When I run this code, the BODY of the page now contains the following content:
HTML: I was appended to BODY!
As you can see, "HTML:" was prepended to the HTML string that we passed into the html() method.
In the above code, when I defined the self-executing function block, I passed in two parameters: the jQuery library (jQuery) and a reference to the core html() method (jQuery.fn.html). These parameters then became arguments for the self-executing function invocation, which allowed for an almost quasi-implicit reference to the original html() method.
From a functional standpoint, there's absolutely no difference between using this approach and just creating a local variable reference to the original method:
var oldHtmlMethod = $.fn.html;
However, there's something that just feels really nice about parameter-argument mapping in this context. Perhaps I just like the fact that the reference-logic was factored out of the override-logic, creating a slightly cleaner separation of concerns.
Want to use code from this post? Check out the license.
Reader Comments
Good idiom - it's nice how everything happens in "one line". A lot of the time, people won't make that "var oldHtmlMethod = $.fn.html;" a local variable either, ie. it will stick around in global scope, which is possibly undesirable, and something this idiom removes.
@Michael,
Good point on leaving stuff in the global scope; using this parameter-argument approach will definitely help keep variables in the preferred scoping.
Nice post. We use this technique to add in extra functionality to jQuery's $.ajax() function. The overridden success argument, for example, looks into the returned JSON object to see if a property called ERROR has been given. That way, we can pass along a localized error message directly from the server without any extra fuss - and even though the ajax request was technically "successful" we prevent the success handler from being run in the case of one of these errors.
I put up a Gist to better explain :)
http://gist.github.com/577713
whoops, problem with that one? this should work http://gist.github.com/577722
Nice technique, I like that :)
But why not do something like this snippet http://pastebin.com/M6wv8AwS
It create one more variable but you won't need to repeat the method name
I believe this is considered "Aspect Oriented Programming" and Paul had a good article on how to do some cool stuff with jQuery (though he uses the much cooler term "duck punching).
Just thought I'd share:
http://paulirish.com/2010/duck-punching-with-jquery/
@Jordan,
Both links worked for me. I like what you're doing. Clearly, this is something very specific to your particular application configuration; but, that said, it looks like it helps unify the API functionality.
@Pomeh,
You could certainly do that; but you still need to create a local variable for the old method reference. Part of what I was demonstrating here was the use of parameter-argument mapping to remove the local variable. Again, no functional difference, just something I liked about it. I like that you don't have to repeat the function name, however - nice idea.
@Alex,
Ha ha, yeah "Duck Punching" is definitely a more catchy phrase.... although, Aspect Oriented Programming sounds very sophisticated.