Binding (and unbinding) JQuery Event Handlers in a Complex Web App

I’m commonly placed in a tough situation. Here I am, working on a web related project and I would like to implement a lot of complex features via javascript. JQuery is the first library I’ve used and it has allowed me to at least dream of a functional, yet complex, web application. Typically, the first 25% of the javascript related work is a total breeze, thanks to jquery. The next 30-50% of the work becomes totally convulated, thanks to jquery being my crutch, and never diving in and bothering to understand javascript all these years. The final percent is ussually left on a wish-list of too-buggy-to-deploy and wouldn’t-it-be-nice.

Here I am again, endeavoring to build a complex and functional web application and this time I really want to get it right, and low and behold, I’m beginning to run into one of those typical problems.

Binding Events with JQuery

JQuery really makes this easy. I love it. I remember first starting off and writing a little bit of code like this:

$('.button').click(function() {
  $.get('/some/stuff', function() {
    $('.somestuff').append(....);
  });
});

and what not. Then I discovered that beloved .load() function. Really cut down on a lot of legwork I was doing. But there was always a slight problem. I always ended up with conflicting binds. There were various ways I worked around this, among other problems before .live() was around, but it was always a bit of a conundrum. I think this mostly deals with the approach JQuery takes towards usability. The focus is quickly accessing an object and binding the specified event, nothing more. It makes no effort to force any sort of design paradigm, which leads me to wonder, how should I handle this? I figure the first place to look for inspiration are in libraries I already currently use that solve this sort of design pattern.

Pyglet Event Handling for OpenGL/Python

I’ve used pyglet for a while now. It has a few design patterns for handling events that go something like this:

# first, it allows for decorators over a method
@windows.event
def mouse_press(...):
    pass # and do stuff

# and also a window method to push handlers of class instances
class SomeStuff(object):
    def mouse_press(...):
        pass # and go

some_stuff = SomeStuff()
window.push_handlers(some_stuff)

# and likewise, you can pop those handlers
window.pop_handlers(some_stuff)

The first design pattern doesn’t really solve any particular problem of mine and is pretty much already what is going on using jquery events. But the second pattern is something I would have considered impossible for me to accomplish in javascript, I’ve just never understood the language enough to consider it doable. But recently, I’ve been coming to understand the language a lot more and wrote a small utility for making better use of prototypal inheritance in javascript, js.js, and it struck me that this would be pretty easy to implement.

A Design Paradigm for JQuery Event Handling

First, its important to understand a small bit about the js.js utility. It’s somewhat based off of Crockford’s function that prototyped an empty constructor with a passed in object. The difference here is that js.js calls magic methods (instead of being an empty constructor) and allows one to pass in multiple objects and functions to be prototyped.

The design consists of two parts. A jquery plugin in the form of $.events.push() and $.events.pop() and a prototype inheritance pattern, optionally using the js.js function.

This is the plugin code:

$.extend({
    events: {
        recurse: function(args, action) {
            for (var x in args) {
                var obj = args[x];
                for (var k in obj) {
                    if (typeof obj[k].bind != "undefined") {
                        if (action == "push") {
                            $(obj[k].bind).bind(k, obj[k], obj);
                        } else {
                            $(obj[k].bind).unbind(k, obj[k]);
                        }
                    } else if (typeof obj[k].live != "undefined") {
                        if (action == "push") {
                            $(obj[k].live).live(k, obj[k], obj);
                        } else {
                            $(obj[k].live).die(k, obj[k]);
                        }
                    }
                }
            }
        },
        push: function() {
            this.recurse(arguments, "push");
        },
        pop: function(obj) {
            this.recurse(arguments, "pop");
        }
    }
});

The scenario is this. A lot of ajax events are very similar and would work across different loaded pages/forms. Where necessary, one could override an event to add extra functionality:

controls = {
  index: function(params) { $(...).load(...); },
  form: function(params) { $(...).load(...); },
  save: function(data) { $.post(..., this.index); }
}

events = {
  keyup: function() { ... },
  submit: function() { ...; this.save(data); return false; }
}
events.keyup.live = '.main .filter'; // just demonstrating access to live and bind
events.submit.bind = '.main .form';

user = function() {
  this.init = function() {
    $('a[href=#user]').bind('click', this.click, this);
  }
  this.click = function() {
    highlight(...);
    // do some more stuff;
    this.index();
    this.form();
    
    $.events.pop(one, two, three);
    $.events.push(this);
  }
}

var User = prototype(user, events, controls, object);

$(function() {
  var u = new User({url: '/db/User'});
});

This allows for common parts to be factored out when shared by many different objects. Picture five other objects alongside User. Some might override events and call the original, for example, events.submit.call(this, …).

This is a little rudimentary to start, but its a start none-the-less.

Advertisements

CherryPy, SndObj, and SVG

Ok so sometime back when i first started tinkering with pysndobj, i was toying with some ideas for a user interface. Primarily i come from a web background and i decided to toy with the idea of a web frontend to a sndobj thread(s) that would write to an output stream that multiple people could connect to to work collaboratively together semi-realtime. Yeah, it sounds a bit amibitious but anyway I decided to take the time to toy around with SVG as well, which ive never worked with, to see what I might come up with.

Ultimately, I got this, cpsndobj.

This is the source code to my simple cherrypy/sndobj/svg demo. Basically I have an svg knob that i made that you can manipulate by clicking and dragging the mouse down on it. Unfortunately with a browser you cannot lock the mouse down in place so it can seem a bit odd if you reach the edge of your screen. But anyways, tar -xzf this bad boy and ./python cpsndobj.py and you will find a local webserver at http://127.0.0.1:8080/. Of course you need to sudo easy_install-2.5 cherrypy if you wanna use it. The rest is javascript(jquery if i remember correctly(been a while since i looked(i think this is why i like python, avoiding all these tags(woot!)))). And I believe I have it set to connect to a jack server so you’ll want to change it to SndRTIO if you want otherwise or start your jack first. After you get it up and running, visit the local address /on to turn on the modulating frequency the visit the root of the site to display the svg knob. Now click and drag it. and youll see it do its thing. Things to note, if your using internet explorer, you’ll need to install the adobe svg viewer (though ive not tested it in IE). Here in firefox land, we apparently like things to run slow so if youd like to experience a smooth svg knob .. experience, then get opera installed and notice the difference.

Uh but yet, i dont use opera past playing with an svg knob and playing flash movies that dont lock up my browser in linux.

One might say, ultimately i basically have a tinker toy thats ok (woot!).

–Edit: I suddenly realized the fallacy of my “i like python” statement above, python still has paranthesis.. duh..