Ask Ben: Overriding Core jQuery Methods
Is there a way to overload the included remove() function in jquery? Possibly a separate js file? For example we are using the min version and it would be cool to not have to add this in each time we upgraded to a new version that has come out?
jQuery is such a well thought out, powerful Javascript library, that it actually uses itself to build itself. What I mean by that is that many of the methods provided by the jQuery library are actually built internally as plugins to the jQuery architecture. When you start to think about the methods in this way, it is a small leap to see that we can override a given jQuery method by simply creating a new plugin of the same name.
Before I demonstrate this, I want to just prefix this with saying that by overriding a core library method, you do run the risk of creating problems when upgrading the library. If you override a method and then don't update it in parallel with the library updates, you might retain bugs that subsequent releases of the library have fixed. That said, in the following demonstration, I am going to override the remove() method by creating a new "remove" plugin:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Overriding jQuery Methods</title>
<script type="text/javascript" src="jquery-1.3.2.js"></script>
<script type="text/javascript">
// Create a closure so that we can define intermediary
// method pointers that don't collide with other items
// in the global name space.
(function(){
// Store a reference to the original remove method.
var originalRemoveMethod = jQuery.fn.remove;
// Define overriding method.
jQuery.fn.remove = function(){
// Log the fact that we are calling our override.
console.log( "Override method" );
// Execute the original method.
originalRemoveMethod.apply( this, arguments );
}
})();
// When DOM is ready, initialize.
$(function(){
$( "a" )
.attr( "href", "javascript:void( 0 )" )
.click(
function(){
// Remove the target link.
$( this ).remove();
// Cancel default event.
return( false );
}
)
;
});
</script>
</head>
<body>
<h1>
Override a jQuery Method
</h1>
<p>
<a>Remove Me 1</a>
<a>Remove Me 2</a>
<a>Remove Me 3</a>
</p>
</body>
</html>
In the above code, we are creating our new "remove" plugin inside of its own closure. This is done so that we can create an intermediary variable - a reference to the original remove() plugin - without colliding with other variables in the global name space. Once we have this pointer, we then override the core remove() method with our new jQuery.fn.remove method. For testing purposes, we are simply logging a note that we are in the new method and then executing the original method.
Of course, if you wanted to override the core version, you wouldn't turn around and call apply() on it - you'd actually move the original remove() logic into our new plugin and then update it as necessary. In this way, you can fully override the core method with whatever logic you see fit. I hope this helps.
Want to use code from this post? Check out the license.
Reader Comments
Nice. Pretty nice Ben.
@William,
Thanks my man. I've never actually done this in production, but it seems straightforward enough.
That's pretty cool. You can even take it a step further and just use a copy of the original jQuery function object to be able to replace multiple functions all at once. I borrowed the clone function from here, http://my.opera.com/GreyWyvern/blog/show.dml/1725165. Then I used it to make a copy of jQuery's functions so that I can call any of the original methods in my new extended version.
Object.prototype.cloneObject = function() {
var newObj = (this instanceof Array) ? [] : {};
for (i in this) {
if (i == 'cloneObject') continue;
if (this[i] && typeof this[i] == "object") {
newObj[i] = this[i].cloneObject ();
} else newObj[i] = this[i]
} return newObj;
};
(function(){
// Store a reference to the original remove method.
var SuperJQ = jQuery.fn.cloneObject();
// Define overriding method.
jQuery.fn.extend({
remove: function(){
// Log the fact that we are calling our override.
console.log( "Override Remove method" );
// Execute the original method.
return SuperJQ.remove.apply( this, arguments );
},
parent: function() {
// Log the fact that we are calling our override.
console.log( "Override Parent method" );
// Execute the original method.
return SuperJQ.parent.apply( this, arguments );
}
})
})();
@Anthony,
Nice suggestion. Using the extend() method is definitely a quality choice, especially if you have more than one method to set / override at a given time.
thanks, this was very useful to me.
Additionally, I will add. (This has nothing to do with your post but relates to my specific situation)
if someone is trying to apply a override for a specific situation
and needs to compare objects, it wont work. (in jQuery)
Apparently jQuery Objects are 'never' identical. (even if they appear to be)
e.g this:
if (this === jQuery('td#elem_pager')){ // will never evaluate to true
// or this of course
if (this == jQuery('td#elem_pager')){ // will never evaluate to true
One will need to use an attribute (or something) such as:
if (this.attr('id') == jQuery('td#elem_pager').attr('id')){ //can evaluate to true
// override core method, re: cant modify 3rd party libs
apply_specific_functionality;
}
else {
// Execute the original method.
originalRemoveMethod.apply( this, arguments );
}
I apologize if this is obvious/rudimentary info, please correct me
if it is incorrect as well.
I also realize this is not good approach as it's not really scalable,
however it will work in my situation for now
@Dwright,
Interesting. I have never tried to compare two jQuery objects, but I guess that makes sense since a new jQuery object is created for each selector.
Hello,
I am trying to use jquery - I am a new at this.
I want to override just one function showLabel in jquery.Validate that is used by xVal.jquery.validate
Is there any simple way to do it?
I've downloaded some inheritence plugin but couldn't implement the sample that i've found to it.
Nevertheless i do need all the functionality to stay the same way , and the showLabel is still being called in jquery.Validate . How can i resolve my situation without werighting all the jquery framework ? :)
@Gregory,
Overriding methods can get a bit tricky inside of a jQuery plugin due to the way in which Javascript methods are scoped. When you define a method, it is lexically scope, meaning it can refer to variables available at the time of the method definition.
When it comes to Javascript inheritance, this type of scoping can cause problems if other methods in the object refer to the method in a way that you could not override (ie. not referencing it via the "this" scope).
The easiest thing for you to do would probably be to create a duplicate of the plugin and go in an update the method manually?
But wan't it create problems when we upgrade to more progressive version of the plugin?
We would have to analyse all our changes from the beggining before changing the plugin , and if we are talking about a long period it will be hell..
So you don't see any way to override a specific function inside a workflow of a plugin ? Maybe some other way that I have not think about?
Won't extend work somehow in this situation ?
Thanks for the quick reply.
I've seen your presentation on jQuery . It's very informative . Thanks !
Do you think that , as you explain in your presentation I could Extend the showLabel function of the jquery.validate and how can i trap the specific inner anonymous function into a var object so i could weright it . And also is there going to be a conflict if i extend the showLabel calling the extension the same name?
thanks a lot for your time and great presentation :)
@Gregory,
You could certainly try to extend the main plugin; but, it depends on how it is wired internally. Some plugin authors go a bit crazy (IMO) with object literals and how things are defined - and it makes it hard to extend in an elegant way.
That said, you should definitely open her up and see what's going on inside; it might be a good place for extension.
@Ben Nadel,
thanks for the reply.
do you have some kind of sample for this kind of thing?
did you work with xVal ?
@Gregory,
Beyond this post, I don't really have much in the way of examples about this.
I think that using this methodology to inject debugging output as you have here is a great tool and a good idea. Aside from that, I would be leery about overwriting core functionality in a production environment.
Changing the core functionality of a library that is so often used in tandem with multiple plugins is an unstable idea to me.
It may be possible to exactly mimic the interface and behavior of the core function while at the same time adding in your own functionality; however, when the core library updates there's always the chance that you'll be revisiting your overwritten method.
@Justin,
It really comes down to how you override the given method. As long as you pass on all given arguments and return the resultant return value, what you do in between shouldn't really matter. The only real problem could come in if other sections of your code somehow only have access to the original method.
I've done inheritance for inner function of some plugin , during using xVal.
It is really simple , after you get the point -
http://2bhere4u.blogspot.com/2009/12/inheritance-in-jquery.html
Hi Ben,
I'm really hope you can help me on this one. I'm trying to override some of the basic functions of jQuery.
What I want is to for example hide some extra fields when a given type of
field is hidden. If you for example tries to hide #bar and #bar has a
css-class of type "foo" then it should also hide #bar_img.
(function() {
var originalHide = jQuery.fn.hide;
jQuery.fn.hide = function() {
this.each(function() {
if (this.className == 'foo')
originalHide.apply( jQuery('#' + this.id + "_img") );
originalHide.apply(this, arguments);
});
return this;
};
})();
Any help is highly appreciated
Thanks and happy new year!
//Casper
@CasperJ,
Is this breaking? At a cursory glance, it appears to be correct.
Hi Ben,
I have a wierd situation. I'm working in a very huge jQuery based project. Functionally, everything is almost done, but now I have few generic requirements to be implemented across all screens. For this, instead of changing all .js files (which are around 500 in number), I was thinking to override $(document).ready function. Do you think this will be a good idea? I think rework would be less, but am not sure of side effects. What do you say?
Here's my implementation:
(function() {
var originalReady = jQuery.fn.ready;
$.fn.extend({
ready: function() {
console.log('custom ready');
originalReady.apply(this, arguments);
}
});
})();
@Krishna,
I don't think there's anything inherently wrong with this; I guess it just depends on how you're going to use it.
Hi Ben,
Thanks for this simple tips. One more suggestion, when calling a parent function, always add a return to it:
return originalHideMethod.apply( this, arguments );
Otherwise, this break the jQuery chain. Doing something like:
$('#foo').hide().remove(); break after the hide...
@David,
Ah, most excellent point. I would say take that to the next level and actually always use a return statement when using call() or apply(). Probably always the best way to make it future-proof.
I tried to override the inArray method using the above example without success.
alert(jQuery.fn.inArray) was [undefined] but alert(jQuery.fn.html) returned the function source for the html method.
I'm not sure what I'm missing here...
@Ben,
The inArray() method is part of the core root jQuery object, not a collection method. You'll need to reference off the main jQuery object:
alert( jQuery.inArray )
Notice that I left out ".fn" which is the collection of methods that get used for the jQuery instances.
Thanks for this! I was already doing most of this, but couldn't get things to work. I didn't know about .apply( ) before now! Thanks!
That is awesome!
One nitpick, put "return" before
originalRemoveMethod.apply( this, arguments );
@Bob,
Glad to help clarify that. apply() and call() are awesome parts of Javascript.
@Barry,
Ahh, good catch! I think I became much better about my return statements once I really got into plugin development.
You should add "return" to your remove() override example.
I just found a piece of code which was built using remove() override, and it messed up jQuery UI animations - elements just disappeared.
I changed the code to the following:
return originalRemoveMethod.apply(this, arguments);
and now jQuery UI works fine.
Thank you so much for the overriding sample. Ironically, it did not work when I tried to override the html() method, however your *OTHER* sample
www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm
worked great.
Perhaps because the second sample function inside the closure gives up a return value, needed for method changing by JQuery, i.e. the error I received was illustrative:
TypeError: 'undefined' is not an object (evaluating 'c("<span></span>").addClass("ui-dialog-title").attr("id",
e).html(d).prependTo')
I just shamelessly copied your other sample (and brazenly changed it) and it worked great.
Many thanks.
Clear and straightforward, thanks!