jLambda: A jQuery plugin for succinct anonymous functions

jLambda on Github

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

8 Responses to "jLambda: A jQuery plugin for succinct anonymous functions"

  1. Evan Carroll Says:

    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.

  2. Bill Zeller Says:

    Good point Evan. Fixed.

  3. kazoolist Says:

    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/

  4. Bill Zeller Says:

    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.

  5. John Tantalo Says:

    $this might make more sense than $l since that is what you're encapsulating, $(this).

  6. dude Says:

    cool! can you put it up on github plz so we can fork/watch it?

  7. Bill Zeller Says:

    I added a link to the github repo above.

  8. Erik Harrison Says:

    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.

Leave a Reply

(Comments will be moderated before being posted)