覆盖jQuery的.val()函数?

33

有没有一种简单的方法可以覆盖 jQuery 的 val() 函数?

我想要覆盖它的原因是每次设置元素的值时都要添加一些处理,而我不想再创建另一个自定义的值设置器,比如 myVal()


我做出这样的假设,即.change()事件在这里对您无效? - Mark Schultheiss
@Mark: 当我使用$('#foo').val('someValue')设置元素的值时,.change()事件没有被触发。只有当用户在GUI /浏览器上更改值时,事件才会触发。 - 7wp
$('#foo').val('someValue').trigger('change'); 可以通过链接 trigger 来使用它。 - Mark Schultheiss
1
@Mark:由于我需要在第一次将值存储到元素中时在各处执行特定的处理,因此我不想在每个使用.val()的地方都添加额外的.trigger()代码。另外,如果我使用.trigger(),那么这将无意中触发一个我不想运行的更改事件,该事件旨在处理来自GUI而不是代码本身的更改事件。按照CMS发布的方式覆盖.val()正是我需要做的事情 :-) - 7wp
6个回答

49
你可以存储原始的val函数的引用,然后覆盖它并进行处理,稍后使用call调用它以使用正确的上下文:
(function ($) {
  var originalVal = $.fn.val;
  $.fn.val = function(value) {
    if (typeof value != 'undefined') {
      // setter invoked, do processing
    }
    return originalVal.call(this, value);
  };
})(jQuery);

请注意,您可以通过检查value参数是否为undefined来区分获取器调用$(selector).val();和设置器调用$(selector).val('new value');


4
这样做不起作用(至少在jQuery 1.7中不起作用)。原始的jQuery函数依赖于参数数量,因此它将始终将原始的val()方法调用视为setter。要修复这个问题,请将“return originalVal.call(this, value);”替换为“return originalVal.apply(this, arguments);”。 - dkl
4
@dkl 提出的更改和原始代码都不能与 Chrome 兼容。使用 if..else 语句测试是否正在获取或设置值可以解决问题。以下是一个透明的覆盖,根据需要进行更改。 (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
这是完美的答案帮助我掌控morris.js onHoverMove事件以更改图例位置。对于return语句,我写了像 let afterOriginalFunc = originalVal.call(this,value); CallMyFunction_DependentOnThatExecution(); return afterOriginalFunc; 这样的代码非常完美,可以执行原始函数并在该函数评估后立即触发我的依赖代码。感谢您提供示例并分享解决方案。 - sanpat

24

我知道这是一个老话题,但它在Google搜索中排名第一,而且答案不完全正确...

例如,如果您在控制台中尝试$('#myinput').val(),您应该得到#myinput的值。

但是,如果您执行$('#myinput').val(undefined),则应该设置#myinput的值!(根据当前答案和评论,您将无法设置#myinput的值)

这里是一个使用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);

如果您想传递所有参数,也可以使用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);

我希望它能够帮到你!


很棒的答案!完美地运行。 - Rajesh
我已经更新了我的答案选择。因为旧的答案已经过时,正如其他人指出的那样,它已经不能工作了。 - 7wp

2

我知道这个问题比较老,但为了给出完整的解决方案,需要正确且分别重写jQuery.val()和jQuery.val(value)。

因为在调用jQuery.val()时,原始的originalVal.call(this, value);无法正常工作。

要以正确的方式获取值,应该像这样做:

originalVal.call(this);

在这里有一个详细解释的网站:http://extremedev.blogspot.com/2012/01/override-jqueryval-and-jqueryvalvalue.html

祝好, Roman


5
谢谢Roman,我已经知道了,但你有点在为自己的网站做宣传。如果你至少把完整的答案也复制到Stack Overflow上,那会更有用。这样,如果你的网站从互联网上消失了,你的答案仍然会保存在这个网站上供人们查看。这将会更有帮助,Roman。虽然感谢你的努力。 - 7wp
如果那段代码能够正常运行就好了!在尝试使用jQuery解析"widgetElementId"之前,它在哪里被定义了? - Steve Childs

1
使用此代码,您可以覆盖特定元素的.val()的“get”和“set”:
(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);
    };

})();

现在您需要在DOM元素上创建两个函数属性 - $val_get$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>

这对于创建协调多个控件的组件非常有用。
JsFiddle: https://jsfiddle.net/oj4gt2ye/6/

1
如果我理解你的意思正确,那么这样做应该可以很好地解决问题:
jQuery.fn.val = function (new_val) {
    alert("You set a val! How wonderful!");
    this.value = new_val;
};

只需确保包括常规功能:获取选择值等。将该代码粘贴在常规jQuery库之后即可。


不要复制原始行为,只需显式调用它以确保向前兼容性和与val()函数的其他覆盖兼容性。 - Martijn
@Martijn:如果两年前我知道现在所知道的,我会这样做。但不幸的是,现在为时已晚。 - Reid

0

我有一些要补充CMS答案的内容,我的实现方式会有所不同,如下:

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

观察$.fn.change.call(this);将在退出val('')函数之前被调用,这将同时触发每个val('')赋值上的.change()。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接