在元素级别覆盖核心 jQuery 函数

Posted

技术标签:

【中文标题】在元素级别覆盖核心 jQuery 函数【英文标题】:Override core jQuery functions on Element level 【发布时间】:2011-08-11 06:17:43 【问题描述】:

是否可以,例如,我想仅在一个 元素上覆盖 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 函数的对象的行为,这对我来说有点过分。

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

【问题讨论】:

【参考方案1】:

我做了这个 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 数据命名空间或担心有人覆盖该键

【讨论】:

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

基于 sdleihssirhc 的回答,但修改为仅适用于先前匹配的元素,因为您说“仅在 ​​one

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

【讨论】:

所以基本上,我仍然需要重写全局 val 函数,这将导致每次调用 val() 函数来检查当前选择的元素是否定义了 custom_val 函数是数据存储。好吧,我承认这是我偶然发现的最干净的方法。我希望有一种方法可以跳过为每个 jQuery 元素修改 val() 我不相信有 - 如果您仍然希望 $("select") 第二次工作,那么您必须修改全局 val() 函数。但是这种方法可以有效地让您覆盖某些元素而不是其他元素。【参考方案3】:

这个小 sn-p 允许覆盖 jQuery 方法。

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

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

【讨论】:

【参考方案4】:

每个 jQuery 对象都包含一个 selector 属性,其中包含(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 实例。

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

【讨论】:

这会因$("select.someClass") 等而中断。如果选择器变得更复杂,它将停止运行。你需要一个稳定的正则表达式来捕捉它。 我同意这是可能的解决方案,这已经在我的脑海中浮现,但我认为这将是矫枉过正,因为我不想在全球层面上搞乱这些核心功能。我有兴趣找到在单个元素上覆盖该函数的解决方案,而不是全局 $.fn 级别【参考方案5】:

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

我们走吧:

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

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

我会编写一个全局函数来捕获 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 */
);

我不知道这是否有效。试试吧。这只是来自我的大脑。

【讨论】:

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

以上是关于在元素级别覆盖核心 jQuery 函数的主要内容,如果未能解决你的问题,请参考以下文章

2.基础核心学习

Jquery2 基础核心

过滤掉 jQuery 结果集中所有具有某个类的父级(在任何级别)的元素

jQuery 在第一“级别”选择元素

第2章 基础核心

js和JQuery入口函数的区别