Home > jquery > How to fulfill your own feature request -or- Duck Punching with jQuery!

How to fulfill your own feature request -or- Duck Punching with jQuery!

February 23rd, 2010

Wait, what? Well first, duck punching is another name for monkey patching. Monkey Patching is a technique to "extend or modify the runtime code of dynamic languages without altering the original source code." But then some Ruby hackers decided that since we rely on duck typing so often, a more proper name was in order:

… if it walks like a duck and talks like a duck, it’s a duck, right? So if this duck is not giving you the noise that you want, you’ve got to just punch that duck until it returns what you expect.[1]


In jQuery we can use this to great effect. We'll employ an AOP-like approach (aspect-oriented programming), maintaining the original function but wrapping it with our enhancement.

Example 1: Adding color support

(This is a silly example, but hopefully it explains the idea. )
You're fully aware that you can use certain color names in HTML. But sadly, everyone's favorite Crayola™ color, Burnt Sienna, can't play along. Well, not until now…

(function($){
 
    var _oldcss = $.fn.css;
 
    $.fn.css = function(prop,value){
 
        if (/^background-?color$/i.test(prop) && value.toLowerCase() === 'burnt sienna') {
           return _oldcss.call(this,prop,'#EA7E5D');
        } else {
           return _oldcss.apply(this,arguments);
        }
    };
})(jQuery);
 
// and using it...
jQuery(document.body).css('backgroundColor','burnt sienna')

Let's walk through that a little bit slower, shall we?

First we start off with a self-executing anonymous function that makes a happy closure while remapping jQuery to $:

(function($){
    // ...
})(jQuery);

Now we save a reference to the old css method as a local variable, and set up our new definition for that method:

    var _oldcss = $.fn.css;
 
    $.fn.css = function(prop,value){
        // ....
    };

Inside our function definition we have an if statement that breaks up two code paths, the new enhanced route, and the old default:

        // if the user passed in backgroundColor and 'burnt sienna'
        if (/^background-?color$/i.test(prop) && value.toLowerCase() === 'burnt sienna') {
 
            // call the old css() method with this hardcoded color value
            return _oldcss.call(this,prop,'#EA7E5D');
        } else {
            // otherwise call the old guy normally, taking his return value and passing it on.
            return _oldcss.apply(this,arguments);
        }

Scroll back up to see the whole thing together. Can you dig it?

Example 2: $.unique() enhancement

$.unique only winnows an array of DOM elements to contain no duplicates, but what if you want all non-duplicates of any type? Here's an upgrade:

(function($){
 
    var _old = $.unique;
 
    $.unique = function(arr){
 
        // do the default behavior only if we got an array of elements
        if (!!arr[0].nodeType){
            return _old.apply(this,arguments);
        } else {
            // reduce the array to contain no dupes via grep/inArray
            return $.grep(arr,function(v,k){
                return $.inArray(v,arr) === k;
            });
        }
    };
})(jQuery);
 
// in use..
var arr = ['first',7,true,2,7,true,'last','last'];
$.unique(arr); // ["first", 7, true, 2, "last"]
 
var arr = [1,2,3,4,5,4,3,2,1];
$.unique(arr); // [1, 2, 3, 4, 5]

A Duck Punching Pattern

Here's the bare pattern. Feel free to use this as the basis of your own:

(function($){
 
    // store original reference to the method
    var _old = $.fn.method;
 
    $.fn.method = function(arg1,arg2){
 
        if ( ... condition ... ) {
           return  .... 
        } else {           // do the default
           return _old.apply(this,arguments);
        }
    };
})(jQuery);

What else could I use this for?

Some other uses I've had or seen for this sort of thing:

So while you're free to file a feature request of jQuery, most of the time you can actually enhance jQuery's methods yourself! Enjoy.

Paul Irish jquery

  1. February 23rd, 2010 at 13:38 #1

    Just be careful when punching ducks in redistributable code (like a plugin). Unless that plugin has been specifically written to provide this functionality, end users might be surprised that jQuery no longer behaves like they expect!

    Indiscriminate modification of core jQuery functionality is akin to modifying Object.prototype, so be careful!

    (see Object.prototype is verboten)

  2. February 23rd, 2010 at 13:42 #2

    @"Cowboy" Ben Alman
    Yeah definitely. It'd be bad idea to run rampant with this technique.
    I will point out that jQuery UI actually duck punches a few of jQuery core's methods… But, clearly, when you do this you run a risk when jQuery ends up implementing a feature that conflicts.
    Thar be dragons!

  3. February 23rd, 2010 at 13:52 #3

    Eerie! I just wrote something similar to this last week :p
    Although I like the name "Duck Punching" more than "Hijacking".
    http://boedesign.com/blog/2010/02/13/hijacking-javascriptjquery-methods/

  4. February 23rd, 2010 at 13:55 #4

    Great reminder that "it's just JavaScript" and there's no reason to be afraid of what hides behind the closure. Provided there are sufficient hooks in the public API, AOP-style techniques are a great forward-compatible way to mod that code NOW!

    On a personal note, I'm glad that I can finally remove my own duck punch for jQuery.live, thanks to jQuery.fn.delegate in 1.4.2. That was your doing, wasn't it?

    PS-There's a bug in your example: burnt sienna is #EA7E5D. Also, cotton candy: #FFBCD9! http://j.mp/aEG56P

  5. February 23rd, 2010 at 14:04 #5

    You mentioned AOP and everything… Perhaps Javascipt programmers are further along on their path to rediscovering computer science than I thought :-P

  6. February 23rd, 2010 at 14:04 #6

    I like this approach, although I've mostly only used it in experimentation; never used it yet in any production code.

  7. February 23rd, 2010 at 14:08 #7

    @Jordan Boesch
    Very cool. I really like the technique.

    @Dave Furfero
    Too true. Prototype.js actually has Function.wrap to facilitate this sort of thing. I'm surprised it's not in ECMA5…

    (Yeah delegate() in 1.4.2 was the result of a lot of people who cared about performance. I'm glad everyone can now share in the result. :)

    Hex updated. Muchos thanks.

  8. February 23rd, 2010 at 14:17 #8

    The approach is used more commonly than some might think, for example, you can't pass an events object to delegate() like you can in bind()… here's a simple fix by Robert Katic to accomplish that:
    http://gist.github.com/310747

    It copies the old method out and over-writes it just like in some of Paul's examples above.

    Now we can run live() and delegate() with event objects :D

    $('ul').delegate('li', {
        click: function(e){
     
        },
        mouseover: function(e){
     
        }
    });
  9. February 23rd, 2010 at 14:23 #9

    In the Taconite plugin I override the httpData function so that I can inspect every ajax response.

    http://github.com/malsup/taconite/blob/master/jquery.taconite.js

  10. coldhead
    February 23rd, 2010 at 20:09 #10

    Obligatory 3D Javascript duck: http://wurstcaptures.untergrund.net/duck.html

  11. February 23rd, 2010 at 21:10 #11

    While I can totally agree this is a nice 'trick,' it is hardly revolutionary for those who have been using pure JavaScript for all of those years before the currently-available JS toolkits/libraries/facades.

    For my part, I find this pattern most useful when writing Greasemonkey scripts – many times I find it useful to hijack/intercept/wrap/redefine/whatever functions a page may already define for my own devious purposes (tongue-in-cheek).

    Using this as a way to extend or 'fix' code provided by toolkits is a good suggestion for those who can't wait for such to be included in official releases.

  12. February 23rd, 2010 at 22:19 #12

    everytime my mind tried to read Duck Punch in the article my mind translated it into Donkey Punch….then I sat there confused as that has nothing to do with JavaScript!!!

  13. February 24th, 2010 at 08:42 #13

    @Jason Bunting
    Yah man I hear ya. I've been using AOP style hacks with greasemonkey for a while myself. I just wanted to re-introduce the concept with a very explicit, but common use case. :)

  14. March 4th, 2010 at 08:33 #14

    This technique is used in the core of Sizzle as well. If the native querySelectorAll method exists Sizzle is duck-punched to use it; otherwise, "oldSizzle" is called.

    http://github.com/jeresig/sizzle/blob/master/sizzle.js#L901

  15. Bernhard
    July 6th, 2010 at 02:50 #15

    I found I had to avoid a null reference in your _getDate override. You might find this useful to protect against nulls:

    if (date && date.setHours && date.setMinutes) {
    date.setHours(inst.selectedHour || inst.currentHour);
    date.setMinutes(inst.selectedMinute || inst.currentMinute);
    }

    Otherwise the new "control" is great. I particularly liked your explicit /*overrides*/function comment. I've not seen that before and it's a superb use of comments.

  16. October 14th, 2010 at 04:00 #16

    This is how many TiddlyWiki plugins work. It's not necessarily a good thing that it's the only way to achieve certain things, but OTOH it shows the power of this approach; that it lets people create plugins to do the kinds of things the creator never anticipated, as with GreaseMonkey scripts.

    Also, it's nice to pass in the old function instead of creating the _localVar to store it. (as in http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm)

  17. October 16th, 2010 at 00:49 #17

    Your unique() implementation could be faster if it used a hash (object/dictonary) to detect an item it has seen before, instead of using a nested loop implied by inArray().

  18. Charles
    March 24th, 2011 at 08:52 #18

    I know this is over a year old now, but if you still see new comments, I'm wondering if you could inform me as to what the difference is between this and using

    jQuery.extend({ css: function( data ) { /* some new stuff */ } } );

    is. I generally see the extend a lot more than I see this method.

  19. March 24th, 2011 at 09:00 #19

    @Charles
    duck punching you retain the original definition of the method.. (and you wrap things before and after it)

    extend you will overwrite it.

  20. Chris
    May 1st, 2011 at 02:35 #20

    Thank you for the $.unique() enhancement! It saves me a lot of time and nerves. Cool Website btw… just painting around and enjoy the result ^^
    Greetings, Chris

  21. JDub
    January 12th, 2012 at 09:58 #21

    When I use the duck punch for $.unique, I keep getting an "arr[0] is undefined" message in console. Am I the only one who can't seem to use that chunk of code? Or am I supposed to do something in addition to pasting that chunk of code in my website?

  1. March 29th, 2010 at 02:22 | #1
  2. April 6th, 2010 at 18:09 | #2
  3. June 9th, 2010 at 00:40 | #3
  4. June 14th, 2010 at 07:13 | #4
  5. March 15th, 2011 at 06:08 | #5
  6. April 21st, 2011 at 09:54 | #6
  7. August 23rd, 2011 at 22:04 | #7

For code blocks, use <pre lang="javascript">. css and html4strict are also accepted.

i left this space here for you to play. <3