I would like to alter the functionality of $.prepend()
(and probably $.append()
) for the purpose of having an "on DOM change event".
Can I do something as simple as:
$.prepend = function() { alert('Hello World'); };
Or do I need to use the $.extend()
function or $.prototype.prepend
or $.fn.prepend
?
[I realise I'll need to include the original source for the prepend()
function in my new one otherwise jQuery will break!]
EDIT :: Final Solution
For those who are interested:
$.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;
};
And usage:
/* 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);
Which method you want to use depends on how much you need to change. Since .prepend
is merely a method that resides in .fn
you don't have to mess with the prototype.
I most cases its enough to rename the original method, create your own function that does what you want and end with a call to the original function, like this:
var orgPrepend = $.fn.prepend;
$.fn.prepend = function(){
// Do what you want here
// Call org prepend and return
return orgPrepend.apply(this, arguments);
}
Note: .apply
and .call
are more or less identical. The only difference is that .apply
passes arguments by reference while .call
passes them by value, so I prefer to use .apply
before .call
where possible. See MDC for reference
But if you look at the source of jQuery (see src/manipulation.js) you'll see that this method is very small so you can just implement it directly.
In the next example I will use .extend
instead, but it's not a must; you could just replace it like in the first example.
$.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 );
}
});
}
});
You can override .domManip
or any other method in the same way, like in the following example. You'll probably see why I prefer to use .extend
here.
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 );
}
});
}
});