33

Is there a way to easily override jQuery's val() function?

The reason I want to override it is that I want to add some processing each time a value is set for an element. And I don't want to make another custom value setter, such as myVal().

Sebastian Zartner
  • 18,808
  • 10
  • 90
  • 132
7wp
  • 12,505
  • 20
  • 77
  • 103
  • I make the assumption that the .change() event does not work for you here? – Mark Schultheiss Jan 13 '10 at 20:23
  • @Mark : When I set the value of elements by doing $('#foo').val('someValue') the .change() event does not get triggered. It only appears to be triggered when the value is changed by the user on the GUI/Browser. – 7wp Jan 13 '10 at 20:28
  • $('#foo').val('someValue').trigger('change'); might be of use to chain the trigger in there then... – Mark Schultheiss Jan 13 '10 at 20:36
  • 1
    @Mark: Since I need to do this particular processing everywhere the first time I store a value into an element, I didn't want to have to add that extra .trigger() code to every single place I use .val(). Also if I use .trigger() then that will inadvertently trigger a change event that I don't want to run, which is intended to process for change events from the GUI not the code itself. Overriding .val() the way that CMS posted is exactly what I need to do :-) – 7wp Jan 14 '10 at 21:20

6 Answers6

49

You can store a reference to the original val function, then override it and do your processing, and later invoke it with call, to use the right context:

(function ($) {
  var originalVal = $.fn.val;
  $.fn.val = function(value) {
    if (typeof value != 'undefined') {
      // setter invoked, do processing
    }
    return originalVal.call(this, value);
  };
})(jQuery);

Note that you can distinguish between a getter call $(selector).val(); and a setter call $(selector).val('new value'); just by checking if the value argument is undefined or not.

Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • 4
    This will not work (at least not in jQuery 1.7). Original jQuery function relies on arguments count, so it will consider the original val() method call always a setter. To fix this, replace "return originalVal.call(this, value);" with "return originalVal.apply(this, arguments);" – dkl Feb 21 '12 at 13:07
  • 4
    Neither the original, nor the proposed change by @dkl works with Chrome. Using an if..else to test whether you're getting or settings does work. Following is a transparent override, change as needed. (function ($) { var originalVal = $.fn.val; $.fn.val = function(value) { if (typeof value == 'undefined') { return originalVal.call(this); } else { return originalVal.call(this, value); } }; })(jQuery); – Martijn Jun 22 '12 at 20:14
  • This is perfect answer helped me to take control on morris.js onHoverMove event to change legend location. For the return stmt, I wrote like `let afterOriginalFunc = originalVal.call(this,value); CallMyFunction_DependentOnThatExecution(); return afterOriginalFunc; ` was a perfect code to execute original function and immidiately trigger my dependent code after that function evaluates. Thanks for the example and sharing the solution. – sanpat Feb 02 '21 at 20:58
24

I know it's an old subject but it's first in google search and the answer is not completely right...

For example if you try in the console $('#myinput').val() you should get the value of #myinput.

But if you do $('#myinput').val(undefined) you should set the value of #myinput!( With the current answer and the comments of it, you will not set the value of #myinput)

Here is an upgraded answer that use arguments.

(function ($) {
  var originalVal = $.fn.val;
  $.fn.val = function(value) {
    if (arguments.length >= 1) {
      // setter invoked, do processing
      return originalVal.call(this, value); 
    }
    //getter invoked do processing
    return originalVal.call(this);
  };
})(jQuery);

if you want to pass all the arguments you can also use apply

(function ($) {
  var originalVal = $.fn.val;
  $.fn.val = function(value) {
    if (arguments.length >= 1) {
      // setter invoked, do processing
    } else {
      //getter invoked do processing
    }
    return originalVal.apply(this, arguments);
  };
})(jQuery);

I hope it help!

2

I know the problem is old but just to give a full solution. In order for both the jQuery.val() and the jQuery.val(value) to work after override you need to override it properly and separately. Because when calling jQuery.val() then originalVal.call(this, value); will not work correctly.

To do it in a correct way you need to do something like that when getting the value: originalVal.call(this);

Here is a site where everything is explained: http://extremedev.blogspot.com/2012/01/override-jqueryval-and-jqueryvalvalue.html

Regards, Roman

Roman
  • 276
  • 2
  • 5
  • 5
    Thanks Roman I already knew that, but you are kind of doing some self promotion here for your site. It would be more useful if you would at least also paste your full answer into stack overflow. That way if your site ever disappears from the internet, your answer would still be intact on this site for people to see. That would be a lot more helpful Roman. Thanks for your effort though. – 7wp Jan 23 '12 at 17:00
  • Would be nice if that code even worked! Where is "widgetElementId" defined before you try and resolve it with jQuery? – Steve Childs Feb 27 '15 at 10:59
1

If I am understanding you right, something like this should do the trick just fine:

jQuery.fn.val = function (new_val) {
    alert("You set a val! How wonderful!");
    this.value = new_val;
};

Just make sure you include the regular functionality: getting values of selects and so on. Just stick that code after after the regular jQuery library.

Reid
  • 18,959
  • 5
  • 37
  • 37
  • Don't copy original behaviour, just call it explicitly to ensure forward compatibility and compatibility with other overrides of the val() function. – Martijn Jun 22 '12 at 20:18
  • @Martijn: If I knew two years ago what I know now, I would have done that. But unfortunately it is a bit late for that. – Reid Jun 22 '12 at 23:43
1

With this code you can override the "get" and "set" of .val() for specific elements:

(function () {

    var __val = $.fn.val;
    $.fn.val = function (value) {
        if (this[0] && (this[0].$val_get || this[0].$val_set)) {
            if (arguments.length === 0) return this[0].$val_get();
            else return this[0].$val_set(value) || this;
        }
        return __val.apply(this, arguments);
    };

})();

Now you have to create two function properties on the DOM element - $val_get and $val_set:

<input type="text" id="myInput" />
<input type="text" id="someOtherInput" />

<script>

    $('#myInput')[0].$val_get = function () {
        console.log('Got value from myInput!');
        return this.value;
    };

    $('#myInput')[0].$val_set = function (value) {
        console.log('Set value of myInput!');
         this.value = value;
    }

    //----

    $('#myInput').val('Hello!'); //Console: "Got value from myInput!"
    $('#myInput').val(); //Hello! | Console: "Set value to myInput!"

    $('#someOtherInput').val('Hello!'); //Console: ""
    $('#someOtherInput').val(); //Hello! | Console: ""

</script>

This is useful for creating components that orchestrate multiple controls.

JsFiddle: https://jsfiddle.net/oj4gt2ye/6/

Raphael
  • 1,847
  • 1
  • 17
  • 29
0

I have something to add to CMS answer, my implementation would differ and be like this:

(function ($) { var fnVal = $.fn.val;
    $.fn.val = function(value) {
        if (typeof value == 'undefined') {
            return fnVal.call(this);
        }
        var result = fnVal.call(this, value);
        $.fn.change.call(this);
        // here you can add some more implementation
        return result;
    };
})(jQuery);

observe the $.fn.change.call(this); will be called before exiting the val('') function this will trigger also the .change() on each val('') assignment.

ungalcrys
  • 5,242
  • 2
  • 39
  • 23