11

Is it possible to override core jQuery functions on Element level? For example, I want to override the 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 a new jQuery instance

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

it stops working because we have a new instance of the JQuery object. If I fiddle with $.fn.val function, I change that behavior for all objects which support the val function, which is a bit too much for me.

How can I achieve that?

5 Answers 5

5

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

Sign up to request clarification or add additional context in comments.

8 Comments

I am wondering where is the variable arguments coming from in this code? Looks like it will be just undefined always. Or can we have $.fn[methodName] = function(arguments) { in fourth line?
@rineez arguments is a special language-level variable that holds the arguments passed to the function being run at the moment. In this case, the arguments are simply being passed on to either the overriding function action or to the originally defined handler function originalVal.
Note that it will change jQuery instance method, so actually it's not working on "element level" as you can have multiple jQuery instances of the same element. I was pretty excited about this till I've realized I'd have to do this each time I'm selecting my custom element. Would be good to add this to some element once, and then have every new jQuery instance of it to have desired method(s) changed.
this is so terrible idea to do! imagine what happens when you override 100 elements in loop like this - pastebin.com/BUmmjMrE - if you do so every single call of .val() will cause calling 100 functions to get a simple value.
@Peter Why in gods name would you ever have 100 nested function overrides? That is really the bad idea. Also, it wouldn't be just getting "a simple value" if you have 100 overrides - you'd obviously be doing 100 crazy things - a very complex function. So no, in reasonable circumstances, no, my answer is not a terrible idea.
|
4

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

2 Comments

So basically, i still need to override global val function, which would result in every call of val() function to check if the currently selected element has custom_val function defined in it's data storage. Well i admit this is the cleanest approach i've stumbled upon jet. I was hoping that there would be a way to skip modifying the val() for every jQuery element.
I don't believe there is - if you still want $("select") to work a second time then you have to modify the global val() function. But this approach effectively lets you override it for some elements and not others.
4

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

Comments

1

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

2 Comments

This would break for $("select.someClass") etc. If the selector becomes more complex it will stop functioning. You'll need a stable regular expression to catch this.
I agree that this is possible solution, and this already crossed my mind, but i've decided that this would be overkill, as i don't want to mess with such core functions on global level. I was interested into finding solution to overwrite that function on a single element, not a global $.fn level
1

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.

2 Comments

It works, i can confirm, but i was hoping to stumble upon some solution which wouldn't require executing additional code for all jQuery objects that support val()
jQuery doesn't have a solution for only override elements-functions. Not that i have hear about. The DATA-Function is for Element saving Data. I think my solution is failsave and fast. Also you can use it for any other function you want to overwrite.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.