Introduction to the Reactive Extensions for JavaScript – jQuery Live Event Integration

So far we’ve come a long way in this series, starting with some of the basics, and then going deeper with composition with such things as the Bing Maps and Twitter Mashup, Drag and Drop, asynchronous programming among others.  One of the nicest aspects so far is the ease of integration into jQuery.  For the first release, we have Observable.FromJQueryEvent, but we didn’t quite cover live events and the integration points that we can extend.  Let’s cover that today and what is coming in a future release of the Reactive Extensions for JavaScript.

Before we get started, let’s get caught up to where we are today:

jQuery Live Event Integration

In an earlier post, we covered the existing jQuery event integration with the bind and unbind scenario.  This is the primary way in jQuery to attach an event handler to existing matched elements such as the following:

$("#someButton").bind("click", function() {
    $(this).attr("disabled", "disabled");
});

And then we could disable the binding such as the following:

$("#someButton").unbind("click");

And then we added a nice abstraction over this to enroll it as an observable via the Rx.Observable.FromJQueryEvent method and the extension to the jQuery fn itself of a ToObservable method.

// FromJQueryEvent
Rx.Observable.FromJQueryEvent($("#someButton"), "click"))
    .Subscribe(function(event) {
        $("#someButton").attr("disabled", "disabled");
    });    


// Or extending jQuery object
$("#someButton")
    .ToObservable("click")
    .Subscribe(function(event) {
        $("#someButton").attr("disabled", "disabled");
    });

But what about if we want to attach a handler to an event for all elements which match the selector both now and in the future?  For example, if we add elements after we have called bind on a matching selector, those will not be bound to the handler, and instead would have to rebind.  In order for us to get the behavior of attaching to all matching selectors both now and in the future, we’ll use the live function which does exactly that.  In this case, we’ll add a a click event handler to all present and future items that have the formButton class which disables the element.

$(".formButton").live("click", function() {
     $(this).attr("disabled", "disabled");
});

$("body")
    .append('<input type="button" class="formbutton" value="Click me!"/>');

Now that we’ve covered how to live bind handlers to events, what about unbinding live events?  To do that, we’ll use the die function which removes all event handlers that we’ve attached using the live function.  This example, taking from the documentation, shows a button bound to click which then live binds to the click handler of the main button.  We also bind the click handler of the unbind button which then unbinds the live handler from the main button.

function aClick() {
    $("div").show().fadeOut("slow");
}

$("#bind").click(function () {
    $("#theone").live("click", aClick)
        .text("Can Click!");
});

$("#unbind").click(function () {
    $("#theone").die("click", aClick)
        .text("Does nothing...");
});

So, how do we capture live events in the Reactive Extensions for JavaScript.  The answer is much like before when we integrated bind and unbind to the Rx.Observable.FromJQueryEvent function.  We’ll take the FromJQuery event and simply replace the calls to bind and unbind with live and die.

Rx.Observable.FromJQueryLiveEvent = function(jQueryObject, eventType, eventData) {

    return Rx.Observable.Create (function(observer) {
        var handler = function(eventObject) {
            observer.OnNext(eventObject);
        };
        jQueryObject.live(eventType, eventData, handler);
        return function() {
            jQueryObject.die(eventType, handler);
        };
    });
};

And we’ll also update the jQuery plugin to be able extend the jQuery object to have a ToLiveObservable method which will look like the following:

jQuery.fn.ToLiveObservable = function(eventType, eventData) {
    return Rx.Observable.FromJQueryLiveEvent(this, eventType, eventData);
}

Now I can rewrite the above sample which uses live and die to use the Reactive Extensions for JavaScript instead.  Instead of calling die explicitly to detach our handlers, we can call Dispose() on our subscription which underneath the covers calls the die function.

var liveEvent = null;

$("#bind")
    .ToObservable("click")
    .Subscribe(function () {
        $("#theone").text("can click...");

        liveEvent = $("#theone")
            .ToLiveObservable("click")
            .Subscribe(aClick);
        });

$("#unbind")
    .ToObservable("click")
    .Subscribe(function () {
            $("#theone").text("Does nothing...");
            
            if (liveEvent != null)
                liveEvent.Dispose();
        });

In addition to this example, we can show off the live aspect of this to show that in fact our observable is live.  This example will enlist the live click event on all paragraphs, timestamp our values, and then add a new paragraph with the associated timestamp.

$("p")
    .ToLiveObservable("click")
    .Timestamp()
    .Subscribe(function(event) {
        $("p").after("<p>Item created " + event.Timestamp + "</p>");
    });

We’re using the Timestamp method to append a timestamp to our value to keep track of time. 

Conclusion

Combining jQuery and the Reactive Extensions for JavaScript gives us even more options to deal with events.  In addition to the standard bind and unbind, we now have the ability to connect and disconnect live events as well.  That’s just one of the many things we can do with it that I’ll hopefully cover more in the near future.  So, download it, and give the team feedback!

What can I say?  I love JavaScript and very much looking forward to the upcoming JSConf 2010 here in Washington, DC where the Reactive Extensions for JavaScript will be shown in its full glory with Jeffrey Van Gogh (who you can now follow on Twitter).  For too many times, we’ve looked for the abstractions over the natural language of the web (HTML, CSS and JavaScript) and created monstrosities instead of embracing the web for what it is.  With libraries such as jQuery and indeed the Reactive Extensions for JavaScript gives us better tools for dealing with the troubled child that is DOM manipulation and especially events.

No Comments