2

我想要改變$.prepend()(可能是$.append())的功能,以便實現「關於DOM更改事件」。使用原型來更改現有jQuery函數的功能

我可以做簡單的東西如:

$.prepend = function() { alert('Hello World'); }; 

或者我需要使用$.extend()功能或$.prototype.prepend$.fn.prepend

[我意識到我需要包括在我的新的,否則jQuery將打破prepend()功能原始出處!]


編輯::最終解決

對於有興趣的人:

$.extend($, { 
domChangeStack: [], 
onDomChange: function(selector, fn, unbind, removeFromStack) { 
    /* Needs to store: selector, function, unbind flag, removeFromStack flag */ 
    jQuery.domChangeStack.push([selector, fn, unbind, removeFromStack]); 
}, 
domChangeEvent: function() { 
    /* Ideally should only affect inserted HTML/altered DOM, but this doesn't */ 
    var stackItem, newStack = []; 

    while (stackItem = jQuery.domChangeStack.pop()) { 
     var selector = stackItem[0], 
      fn = stackItem[1], 
      unbind = stackItem[2], 
      remove = stackItem[3]; 

     if (unbind) { $(selector).unbind(); } 
     // Need to pass the jQuery object as fn is anonymous 
     fn($(selector)); 
     if (!remove) { newStack.push(stackItem); } 
    } 

    jQuery.domChangeStack = newStack; 

    // Show something happened! 
    console.log("domChangeEvent: stack size = " + newStack.length); 
} 
}); 

$.fn.prepend = function() { 

    var result = this.domManip(arguments, true, function(elem) { 
     if (this.nodeType === 1) { 
      this.insertBefore(elem, this.firstChild); 
     } 
    }); 

    // Need to actually alter DOM above before calling the DOMChange event 
    $.domChangeEvent(); 

    return result; 
}; 

和用法:

/* Run the given function on the elements found by the selector, 
* don't run unbind() beforehand and don't pop this DOMChange 
* event off the stack. 
*/ 
$.onDomChange(".element_class", function(jObj) { 
     jObj.do_something_awesome(); 
    }, false, false); 

回答

3

要使用哪種方法取決於您需要更改多少。由於.prepend只是一種駐留在.fn中的方法,因此不必混淆原型。

我大多數情況下,它足以重命名原始方法,創建自己的功能,你想要做什麼,它與原始函數調用結束了,像這樣:

var orgPrepend = $.fn.prepend; 
$.fn.prepend = function(){ 
    // Do what you want here 

    // Call org prepend and return 
    return orgPrepend.apply(this, arguments); 
} 

注:.apply.call或多或少是相同的。唯一的區別是.apply通過引用傳遞參數,而.call通過它們的值,所以我傾向於在可能的情況下在.call之前使用.applySee MDC for reference

但是,如果你看看jQuery的源代碼(見src/manipulation.js),你會發現這個方法非常小,所以你可以直接實現它。

在下一個例子中,我將使用.extend來代替,但它不是必須的。你可以像第一個例子那樣替換它。

$.extend($.fn, { 
    prepend: function() { 
     // Do what you want here 
     console.log("prepend was called"); 
     // Execute domManip 
     return this.domManip(arguments, true, function(elem) { 
      if (this.nodeType === 1) { 
       this.insertBefore(elem, this.firstChild); 
      } 
     }); 
    } 
}); 

您可以用同樣的方法覆蓋.domManip或任何其他方法,如下例所示。你可能會明白爲什麼我喜歡在這裏使用.extend

var _domManip = $.fn.domManip; 
$.extend($.fn, { 
    domManip: function() { 
     // Do what you want here 
     console.log("domManip was called"); 
     _domManip.apply(this, arguments); 
    }, 
    prepend: function() { 
     // Do what you want here 
     console.log("prepend was called"); 
     // Execute domManip 
     return this.domManip(arguments, true, function(elem) { 
      if (this.nodeType === 1) { 
       this.insertBefore(elem, this.firstChild); 
      } 
     }); 
    } 
}); 

test case on jsFiddle

2

我做了一個小測試,看來你必須替換$.fn.prepend。無論如何,如果你願意,你可以這樣做:

var old=$.fn.prepend; 
$.fn.prepend=function(){ 
    //Trigger DOMchange event 
    old.apply(this, arguments); 
}; 

通過這種方式,你可以在無需重寫功能代碼一個新包裝老前置功能。

+1

你打我吧! ;) – mekwall 2011-05-19 09:15:46

+0

兩個正確的答案比沒有答案好:) – mck89 2011-05-19 09:26:06

+0

當然。儘管主要問題本身已得到解答,但我傾向於詳細說明我的答案,以使其通用。希望能幫助其他人提出類似的問題:) – mekwall 2011-05-19 09:29:56