SNag
SNag

Reputation: 18141

Override jQuery functions simply by extending?

This is related to, but not a duplicate of, another SO Q&A Override jQuery functions.

It is clear from the answer to the above question that the pattern to override a jQuery function is:

(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);

But why!?

I've been able to override a jQuery function simply by defining a function of the same name as the function to be overridden, within $.extend or $.fn.extend.

Consider this:

// random example showing jquery function overriding
$.fn.extend({
    hide: function() {
        $(this).css({"color":"red"});
    }
});

$("#test").hide(); // this will actually paint the #test element red!

jsFiddle

I'd like to understand why _old.apply(this,arguments) would be the preferred way to override a jQuery function, as listed here and here.

Upvotes: 3

Views: 2673

Answers (2)

Mohit Pandey
Mohit Pandey

Reputation: 3823

(function($){

    // store original reference to the method
    // stored locally
    var _old = $.fn.method;

    $.fn.method = function(arg1,arg2){

        if ( ... condition ... ) {
           return  ....
        } else {           // do the default
           // call apply method, in order to pass the this context.
           return _old.apply(this,arguments);
        }
    };
})(jQuery);

Here in the above code, we are calling an anonymous function, in which we are declaring a local variable _old. When this anonymous function execute, it save the _old method reference and form a closure. Now, when we call the new method, i.e,

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

we also have an access to _old method, since its scope exists in the current context. And then, we can use it inside the new method.

Here we are calling _old method with the help of apply, because we want to have the same this context for that as well.

With this approach, we can easily override the jQuery method by preserving its original functionality.

Upvotes: 2

guest271314
guest271314

Reputation: 1

From glancing at references provided at original post, summary of pattern could be to keep both "old" and "new" methods available ?

Edit, updated

Sorry, I don't get this. As far as I see, the reference to the overridden method is saved in a local variable in a closure is unquestionably lost outside the closure. Can you explain how the "old" method is still available? –SNag

I'd like to understand why _old.apply(this,arguments) would be the preferred way to override a jQuery function, as listed here and here.

Utilizing pattern at 1st link , above , if interpret pieces correctly, appear arguments test within if statement of jquery method within "self-executing anonymous function" determine return value of "old" or "new" (newly included; override) jquery method ?

i.e.g., try

html

<div>abc</div>

js

// See http://www.paulirish.com/2010/duck-punching-with-jquery/ , at
// `First we start off with a self-executing anonymous function,
// that makes a happy closure while remapping jQuery to $:`

// `closure` start
    (function ($) {
        // jquery `.css()`
        var _oldcss = $.fn.css;
        // jquery `.hide()`
        var _oldhide = $.fn.hide;

        // "new" `.css()`
        $.fn.css = function (prop, value) {
            // "new" `.css()` `test`
            if (/^background-?color$/i.test(prop) 
                && value.toLowerCase() === 'burnt sienna') {
                return _oldcss.call(this, prop, '#EA7E5D');
            } else {
                return _oldcss.apply(this, arguments);
            }
        };

        // "new" `.hide()`
        $.fn.hide = function (prop, value) {
            // "new" `.hide()` `test` 
            if (/color/i.test(prop) && /[a-f]|[0-9]/i.test(value)) {
                return $.fn.css.call(this, prop, value);
            } else {
                return _oldhide.apply(this, arguments);
            }
        };
    })(jQuery);
    // `closure` stop

    // and using it...
    // "new" `.css()`
    jQuery(document.body).css('backgroundColor', 'burnt sienna');
    // "old" `.css()`
    $("div").css("color", "yellow");
    // "old" `.hide()`
    $("div").hide(7500, function () {
        // "old" `.css()`
        $(document.body)
            .css({
            "transition": "background 2s",
            "background": "#edd452"
        })
        .find($("div")).show(2500)
        // "new" `.hide()`
        .hide("color", "red")
    });

jsfiddle http://jsfiddle.net/guest271314/5bEe4/

Upvotes: 1

Related Questions