Today I was trying to decouple some JavaScript classes in a game prototype I've been working on. I didn't want to get into implementing some kind of interface behaviour, and so I thought about a simple Observer pattern. There are a bunch of solutions around the tubes (and indeed I'll probably use one of them) but I started wishing I could just use jQuery's custom events directly on my JavaScript classes.
This idea seemed pretty wacky to me, and made me smile: jQuery events attach themselves to DOM elements - wrapping a JavaScript object in the jQuery selector seemed ludicrous - I mean, should I be able to do $(class).hide()?! No, of course not. But none the less, my curiosity got the better of me, and I tried this lil' snippet:
function cPerson( name, age ){
this.name = name;
this.age = age;
$( this ).bind( "yell", function(){
alert( this.name + " is " + this.age + " years old!!" );
})
}
var person1 = new cPerson( "Bob", 32 );
$( person1 ).trigger( "yell" );
To my astonishment, it worked! The custom event fired! I quickly delved into the jQuery source to find out how it handled the object. Skipping through all the regular selector handlers, our object gets converted into an array like so:
return this.setArray(jQuery.isArray( selector ) ?
selector :
jQuery.makeArray(selector));
Which is also how other objects like "document" and "window" get handled. Ahh yeah, document and window... it's not that ludicrous after all. I tested it on functions which, I reasoned, should also work - but alas there is a path in the jQuery selector code that says:
if ( jQuery.isFunction( selector ) )
return jQuery( document ).ready( selector )
So functions fall through to the document.ready shortcut code. Ah well.
But wait, what else can we do with a wrapped instance of an object? Obviously "slideDown()" is a bit stupid? And clone(), appendTo(), wrapAll() etc. don't make sense... how abooouut... data? or plugins?!
$.fn.misterise = function(){
return this.each(function() {
this.name = "Mr " + this.name;
this.name += $(this).data( "surname" ) || "";
});
};
var person1 = new cPerson( "Bob", 32 );
var person2 = new cPerson( "Dave", 27 );
var peeps = [ person1, person2 ];
$( peeps )
.data( "surname", " Dobalina" )
.misterise()
.trigger( "yell" );
And out pops Mr Bob Dobalina & Mr Dave Dobalina! I'm not sure of any practical purpose for this odd bit of functionality. Perhaps some kind of AOP meta-data system or, um, no... I dunno. Here's a plugin for executing methods on an object by name:
$.fn.go = function( funcName ){
return this.each(function(){
if( $.isFunction( this[ funcName ] ) ){
this[ funcName ]();
}
});
};
That's pretty stupid and cool. Using a plugin against your JavaScript classes instead of a simple method call will most likely get you killed by your colleagues, but hey, it keeps things looking jQuery-ish. And that's the important thing.
Update: Futher investigation has lead to some interesting discoveries... Go have a read!
2 Comments
Very useful! I didn’t know this.
I definitely will use the “go” function in my upcoming projects, I just added the “arg” parameter to handle over parameters similar to the standard “apply” function.
Have Fun !
Rusco
$.fn.go = function( funcName, args ){
return this.each(function(){
if( $.isFunction( this[ funcName ] ) ){
this[ funcName ](args);}
});
};
Nice work Rusco! I’m scared to see where this might possibly lead ;)