Override core jQuery functions on Element level

2019-04-05 02:56发布

问题:

Is it possible to override core jQuery functions on Element level, so for an example, i want to override val() function only on one <select> element.

if i do something like this

var element = $('select');
var old_val = element.val;
element.val = function () {
  console.log('The new val');
  return old_val.apply(this, arguments);
}
element.val(19);

it works as expected, but as soon as i address the same field with new jQuery instance

var element = $('select');
element.val(19);

it stops working because we have new instance of jQuery object. if i fiddle with $.fn.val function i change that behavior for all objects who support val function, which is a bit to much for me.

Any help will be appreciated. Thank you.

回答1:

I made this jquery extension for doing just what you're talking about:

// overrides a method thats supposed to be called on a single node (a method like val)
$.fn.overrideNodeMethod = function(methodName, action) {
    var originalVal = $.fn[methodName];
    var thisNode = this;
    $.fn[methodName] = function() {
        if (this[0]==thisNode[0]) {
            return action.apply(this, arguments);
        } else {
            return originalVal.apply(this, arguments);
        }
    };
};

Bonus over Dave's answer - you don't have to pollute the jquery data namespace or worry about someone overwriting that key



回答2:

Based on answer by sdleihssirhc but modified to only apply to the previously matched element(s), since you said "only on one <select> element".

element.data('custom_val', function() {
    console.log('The new val');
});

$.fn.old_val = $.fn.val;
$.fn.val = function () {
    var custom_val = this.data('custom_val');
    if (custom_val) {
         return custom_val.apply(this, arguments);
    } else {
         return this.old_val.apply(this, arguments);
    }
};


回答3:

This little snippet allows to override jQuery methods.

I find @BT answer quite bad as it creates extra callback every time function is used. So if you'll apply it to 1000 elements, 1000 functions for EVERY val() are called!

My overrideNodeMethod can be applied to as many elements as many times as you want. It basically checks hasOverriddenMethod data

var originaljQueryMethods = {};
for(var method in $.fn){        
    originaljQueryMethods[method] = $.fn[method];
};

$.fn.callOriginalMethod = function(method, args) {        
    return originaljQueryMethods[method].apply(this, args);
};        

$.fn.overrideNodeMethod = function(method, callback) {                
    var dataKey = "hasOverriddenMethod." + method;
    $(this).callOriginalMethod("data", [dataKey, callback]);
    $.fn[method] = function() {
        var callback = $(this).callOriginalMethod("data", [dataKey])
        if (callback) {
            return callback.apply(this, arguments);                
        }
        return $(this).callOriginalMethod(method, arguments);            
    };
};


回答4:

Every jQuery object includes a selector property, which contains (duh) the original selector that was used to get the elements (I haven't really worked with it at all, so I'm not sure what happens to it as you go through a chain of methods).

So you could wrap the val method in a function that checks the selector property, and then decides whether to use your function or the original. It would look something like this:

$.fn.old_val = $.fn.val;
$.fn.val = function () {
    if (this.selector === 'select') {
        console.log('The new val');
    }
    return this.old_val.apply(this, arguments);
};

You basically had this before, I'm just tweaking it so it will apply to all new jQuery instances.

(Also, this is pretty simplistic on it's own; you'll want to modify/enhance it accordingly.)



回答5:

Yeah this a great question. I'd never try this thing.

Let's go:

Before we start we should copy the default val-function to another place:

jQuery.fn.oldval = jQuery.fn.val;

I would write a function global to catch the val() function:

jQuery.fn.val = function(value) {
   var t = jQuery(this);
   if(t.get(0)) {
      //Check for overwritten function
      var func = jQuery.data(t.get(0), 'val-function');
      if(jQuery.isFunction(func)) {
         return func(value);
      }
   }
   //Use old function
   return jQuery(this).oldval(value);
};

After that you can set a function to your Element-Data with:

var element = jQuery(...);
jQuery.data(element.get(0), 'val-function', function(value){
   /* Your Function here */
});

I don't know if this works. Try it. It's only from my brain.