Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Joel Hill and Matt Vickers and Shawn Grigson and Jonathan Rowny and Jonathan Dowdle and Christian Ready and Oscar Arevalo and Jeff McDowell and Steve 'Cutter' Blades
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Joel Hill@Jiggidyuo ) , Matt Vickers@envex ) , Shawn Grigson@shawngrig ) , Jonathan Rowny@jrowny ) , Jonathan Dowdle@jdowdle ) , Christian Ready@christianready ) , Oscar Arevalo@oarevalo ) , Jeff McDowell@jeff_s_mcdowell ) , and Steve 'Cutter' Blades@cutterbl )

Using Self-Executing Function Arguments To Override Core jQuery Methods

By Ben Nadel on

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.




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

@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.