jLambda: A jQuery plugin for succinct anonymous functions
Download
Production (YUI compressed, 448 bytes)
Development (1.3kb)
Demo
View a demo here
Motivation
jQuery has an elegant chaining syntax that allows much of its API to be expressed in a clear, succinct way. For example, the following code from the jQuery homepage code finds all <p> elements with the class neat. The class "ohmy" is added to each matching element and then show() is called, which causes each element to be displayed.
$("p.neat").addClass("ohmy").show("slow");
Many jQuery functions accept a function as a argument which is called after some action is performed. For example, the following code finds all elements with the class "foo" and tells jQuery to call the function 'clkCallback' each time such an element is clicked.
function clkCallback() {
$(this).hide();
}
$('.foo').click(clkCallback);
These functions are often written anonymously:
$('.foo').click(
function() {
$(this).hide();
});
Often one wants to register a callback function that calls another jQuery function (like in the above, where the goal is to call hide() on the clicked element). Javascript's verbose anonymous function syntax makes this difficult to do succinctly. Can we do better?
The Plugin: jquery.lambda.js
I've written a jQuery plugin which adds the succinctness of jQuery chaining to callback functions. Generally, the plugin adds the global function $l (lowercase L, for "lambda"), which "wraps up" a jQuery expression into a function that can be executed later. For example, instead of writing:
function() {
$(this).hide().addClass('foo').css('color', '#fff')..;
}
The plugin allows you to write:
$l.hide().addClass('foo').css('color', '#fff')..;
This allows succinct expression of callback functions.
// Without plugin:
$('.foo').click(
function() {
$(this).hide();
});
// With plugin:
$('.foo').click($l.hide());
Usage
By default, any expressions following $l are called with $(this) as the argument. The plugin also makes the jQuery $() function available. For example:
// Without plugin:
$('.foo').click(
function() {
$(this).hide();
$('p').show();
$('a').width(20);
});
// With plugin:
$('.foo').click($l.hide().$('p').show().$('a').width(20));
More generally, calling:
$l.a().b().$('x').c().$('y').d().$('z').e().f().g();
is functionally equivalent to:
function() {
$(this).a().b();
$('x').c();
$('y').d();
$('z').e().f().g();
}
Supported Methods
All properties of jQuery.fn are available, as are some additional ajax methods (ajax, get, getJSON, getScript, and post). These ajax methods can be called with, i.e., $l.$.ajax().
The plugin also supports a special function called ret(), which controls the value returned from the generated function.
// returns 5 when called.
$l.addClass('x').ret(3).css('color', '#f00').ret(5).show();
Performance
From a size/download perspective, the plugin is only 448 bytes (when compressed with the YUI Compressor).
From a performance perspective, the cost of using jLambda will be proportional to the number of chained calls made to $l (but not the size of the document or the DOM tree). Since Javascript doesn't support dynamic getters (see PHP's __get or Python's __getattr__), the values from the jQuery.fn need to copied after each called to a "placeholder" function, to allow that function to at as a dictionary to be called.
Compatibility
jLambda has been tested with Firefox 3.0.14, Firefox 3.5.3, Google Chrome 3.0.195.27, Safari 4.0.3 (5531.9), IE 8.0.7600.16385
Tue, 27 Oct 2009
Courier is a really bad choice of fonts for "l" (lowercase L) and "1" (one) differentiation, they both have serifs in Courier, and 99.9% of people will be confused at the Serif in "l" (lowercase L). The "l" (lowercase L) in courier has no tilt, and the "1" (one) has a ~15 deg tilt. You might want to make it explicit, because I also thought this was the numeral one at first.
Tue, 27 Oct 2009
Good point Evan. Fixed.
Tue, 27 Oct 2009
Re: "Since Javascript doesn't support dynamic getters"
They aren't as generic as PHP's __get() handler (so, I'm not sure if they'd solve your specific problem here), but JavaScript 1.5 does support "dynamic getters" generally:
https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Creating_New_Objects/Defining_Getters_and_Setters
http://ejohn.org/blog/javascript-getters-and-setters/
Tue, 27 Oct 2009
kazoolist: Javascript 1.5 supports getter/setting functions, but these require that you know the name of the property before hand. By "dymanic", I was referring to the ability to define one function that would handle references to all attribute/properties.
Tue, 27 Oct 2009
$this might make more sense than $l since that is what you're encapsulating, $(this).
Wed, 28 Oct 2009
cool! can you put it up on github plz so we can fork/watch it?
Wed, 28 Oct 2009
I added a link to the github repo above.
Tue, 3 Nov 2009
Little embarrassed - not hours ago I wrote similar, though less featureful code.
I also used $this, as John suggested. Made sense in my case, since my code only supported instance methods. It really is clearer what's going on than $l, but I see why you picked what you did.
Cool stuff.