问题 覆盖元素级别的核心jQuery函数


是否有可能在元素级别覆盖核心jQuery函数,所以举个例子,我想覆盖 VAL() 功能只在一个 <选择> 元件。

如果我做这样的事情

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

它按预期工作,但只要我用新的jQuery实例处理相同的字段

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

它停止工作,因为我们有jQuery对象的新实例。如果我摆弄 $ .fn.val 函数我改变了支持val函数的所有对象的行为,这对我来说有点太多了。

任何帮助将不胜感激。 谢谢。


4659
2018-04-22 22:16


起源



答案:


我做了这个jquery扩展来做你正在谈论的事情:

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

奖励Dave的答案 - 您不必污染jquery数据命名空间或担心有人覆盖该密钥


4
2017-11-13 01:26



我想知道变量在哪里 arguments 来自这段代码?看起来它总是会被定义。或者我们可以拥有 $.fn[methodName] = function(arguments) {  在第四行? - rineez
@rineez arguments 是一个特殊的语言级变量,它保存传递给当前正在运行的函数的参数。在这种情况下,参数只是被传递给重写函数 action 或者最初定义的处理函数 originalVal。 - B T
请注意,它将更改jQuery实例方法,因此实际上它不能处理“元素级别”,因为您可以拥有相同元素的多个jQuery实例。我对此感到非常兴奋,直到我意识到每次选择自定义元素时我都必须这样做。最好将它添加到某个元素一次,然后让它的每个新jQuery实例都改变所需的方法。 - pie6k
这太可怕了! 想象当你在循环中覆盖100个元素时会发生什么 - pastebin.com/BUmmjMrE  - 如果这样做,每次调用.val()都会导致调用100个函数来获得一个简单的值。 - Peter
@Peter为什么在众神名称中你会有100个嵌套函数覆盖?这真是个坏主意。而且,如果你有100个覆盖,它不仅仅是获得“一个简单的价值” - 你显然会做100个疯狂的事情 - 一个非常复杂的功能。所以不,在合理的情况下,不,我的回答并不是一个糟糕的主意。 - B T


答案:


我做了这个jquery扩展来做你正在谈论的事情:

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

奖励Dave的答案 - 您不必污染jquery数据命名空间或担心有人覆盖该密钥


4
2017-11-13 01:26



我想知道变量在哪里 arguments 来自这段代码?看起来它总是会被定义。或者我们可以拥有 $.fn[methodName] = function(arguments) {  在第四行? - rineez
@rineez arguments 是一个特殊的语言级变量,它保存传递给当前正在运行的函数的参数。在这种情况下,参数只是被传递给重写函数 action 或者最初定义的处理函数 originalVal。 - B T
请注意,它将更改jQuery实例方法,因此实际上它不能处理“元素级别”,因为您可以拥有相同元素的多个jQuery实例。我对此感到非常兴奋,直到我意识到每次选择自定义元素时我都必须这样做。最好将它添加到某个元素一次,然后让它的每个新jQuery实例都改变所需的方法。 - pie6k
这太可怕了! 想象当你在循环中覆盖100个元素时会发生什么 - pastebin.com/BUmmjMrE  - 如果这样做,每次调用.val()都会导致调用100个函数来获得一个简单的值。 - Peter
@Peter为什么在众神名称中你会有100个嵌套函数覆盖?这真是个坏主意。而且,如果你有100个覆盖,它不仅仅是获得“一个简单的价值” - 你显然会做100个疯狂的事情 - 一个非常复杂的功能。所以不,在合理的情况下,不,我的回答并不是一个糟糕的主意。 - B T


根据sdleihssirhc的回答,但修改为仅适用于以前匹配的元素,因为你说“只有  <select>元素“。

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

4
2018-04-22 22:30



所以基本上,我仍然需要覆盖全局 VAL 函数,它将导致每次调用val()函数来检查当前所选元素是否具有在其数据存储中定义的custom_val函数。我承认这是我偶然发现喷气机最干净的方法。我希望有一种方法可以跳过修改 VAL() 对于每个jQuery元素。 - Goran Radulovic
我不相信有 - 如果你仍然希望$(“select”)再次工作,那么你必须修改全局val()函数。但是这种方法有效地允许您为某些元素而不是其他元素覆盖它。 - Dave James Miller
仅供参考我是偶然编辑这篇文章而不是我的 - 这不会让我感到震惊...... - B T


这个小片段允许覆盖jQuery方法。

我发现@BT的答案相当糟糕,因为它在每次使用函数时都会创建额外的回调。因此,如果你将它应用于1000个元素,每个函数1000个 val() 叫做!

我的 overrideNodeMethod 可以根据需要多次应用于多个元素。它基本上是检查 hasOverriddenMethod 数据

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

2
2018-02-09 15:06



值得我投票 - Marco Alves
是否可以评论您的代码?谢谢 - Marco Alves


每个jQuery对象都包含一个 selector property,包含(duh)用于获取元素的原始选择器(我根本没有真正使用它,所以我不确定当你通过一系列方法时会发生什么)。

所以你可以把它包起来 val 检查函数的函数中的方法 selector 属性,然后决定是使用您的功能还是原始功能。它看起来像这样:

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

你之前基本上有这个,我只是调整它,所以它将适用于所有新的jQuery实例。

(另外,这本身就非常简单;你需要相应地修改/增强它。)


1
2018-04-22 22:23



这会打破 $("select.someClass") 如果选择器变得更复杂,它将停止运行。你需要一个稳定的正则表达式来捕捉它。 - Raynos
我同意这是可能的解决方案,这已经超出了我的想法,但我已经决定这将是过度的,因为我不想在全球范围内搞乱这些核心功能。我有兴趣找到在单个元素上覆盖该函数的解决方案,而不是全局$ .fn级别 - Goran Radulovic


是的,这是一个很好的问题。我永远不会尝试这件事。

我们走吧:

在开始之前,我们应该将默认的val函数复制到另一个地方:

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

我会写一个函数global来捕获val()函数:

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

之后,您可以使用以下命令为Element-Data设置一个函数:

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

我不知道这是否有效。尝试一下。它只来自我的大脑。


1
2018-04-22 22:30



它可以工作,我可以确认,但我希望偶然发现一些不需要为所有支持val()的jQuery对象执行额外代码的解决方案 - Goran Radulovic
jQuery没有只覆盖元素 - 函数的解决方案。不是我听说过的。 DATA-Function用于元素保存数据。我认为我的解决方案是失败和快速。您也可以将它用于要覆盖的任何其他功能。 - Manuel Richarz