JavaScript Behavior Sheets: an experiment

(c) Bertrand Le Roy 2004 Here’s a little experiment. I’m really after feedback on this one as I’m trying to decide whether this is a good idea. It’s also entirely possible somebody else did this before. That would be good feedback too. Anyway, here it is.

Despite its shortcomings, CSS has a number of features that make it very compelling. First, it decouples styling from markup. Second, its selector syntax is simple, yet reasonably powerful.

So we have semantic markup on the one hand, and styles on the other hand, and the only coupling between the two is the selectors in the stylesheet.

In Ajax applications, there is a third kind of entity in the mix, JavaScript behavior. There are of course ways to decouple the script behavior from the markup, which are usually referred to as unobtrusive JavaScript. jQuery also introduced back in 2005 a way to associate script behavior with the DOM using the same selector syntax that CSS uses.

But way before jQuery, Internet Explorer 5 enabled developers to specify behavior in stylesheets. You could do this for example:

.hilite { behavior:url(hilite.htc) }

This was a pretty neat idea at the time despite the challenges that came with it (in terms of performance for example), but it was never adopted by the other browsers despite having been submitted to W3C. The feature was never very widely used. There *is* a Firefox implementation of HTC behaviors but it doesn’t seem to have helped much in terms of adoption of this feature.

And one thing that bothers me with that idea is that while it does decouple behavior from markup, it also couples behavior to styling at least in location. Putting styles and behavior in the same file, in retrospect, looks like one step forward, and one step backwards. Sure, you could always use two separate files but the system did nothing to encourage you to do that.

What I’m trying to do here is separate styling, markup *and* behavior, leverage selectors in a nice, declarative way à la CSS and still work on all modern browsers. The coupling mechanism between style and markup (CSS selectors), is reasonably good and is already known by all Web developers so it remains a natural choice as jQuery showed clearly. Let’s see if one can express behavior in a way that is close to the already well-known CSS pattern.

In this implementation, it is possible to add event handlers and Microsoft Ajax behaviors from a “JavaScript Behavior Sheet” which can be embedded in the page or be loaded from a separate file using a modified script tag:

<script type="text/behavior" src="BehaviorSheet.jss"></script>

The contents of the tag or file is in the simple JSON notation for an object, omitting the curly braces:

"input[type=text].nomorethanfive": {
    click: function(e) {
        alert("You clicked input #" + e.target.id);
    },
    "Bleroy.Sample.CharCount": {
        maxLength: 5,
        overflow: function(source, args) {
            $(source.get_element()).jFade({
                 property: 'background',
                 start: 'FFFF00',
                 end: 'FFFFFF',
                 steps: 25,
                 duration: 30
            });
        }
    }
}

The top-level entities that can be found in there are the CSS selectors (note the quotes that are a departure from CSS notation but made the prototyping so much simpler). Each selector is associated with an object that contains event and object definitions.

The event definitions consist in the event name and the handler to associate with it:

click: function(e) {
    alert("You clicked input #" + e.target.id);
},

The implementation of this feature uses the new live events from jQuery. The result of that definition is that clicking on any input of type text with the class “nomorethanfive” will display an alert giving the id of the input that was clicked. That is a pretty efficient way to hook up events to multiple elements…

The behavior instantiation specifies the class to instantiate, “Bleroy.Sample.CharCount” and lists the properties, fields and events to set (here, the maxLength property is set to 5 and the overflow event is hooked to a function that flashes the element’s background yellow):

"Bleroy.Sample.CharCount": {
    maxLength: 5,
    overflow: function(source, args) {
        $(source.get_element()).jFade({
             property: 'background',
             start: 'FFFF00',
             end: 'FFFFFF',
             steps: 25,
             duration: 30
        });
    }
}

Here’s what the page looks like:

I should point out that while the events will handle DOM mutations (such as adding new elements that match the selector) just fine, component instantiation won’t in this implementation, which is a limitation that is quite hard to work around in current browsers.

So here it is. All the code for this is available from the link below (contains jFade, code licensed under MIT: jQuery, and code under MS-PL: Microsoft Ajax and my own code), with some tests written with RST.js. So what do you think?

http://weblogs.asp.net/blogs/bleroy/Samples/BehaviorSheet.zip

UPDATE: interestingly, the intent behind Reglib by Greg Reimer is pretty close to this and he even uses the words “Behavior Sheets”.

UPDATE: Stuart Langridge did something very close to declarative events as they are done here back in November 2003. Thanks to Clayton for pointing that out. One should note that the limitations of the time are no longer preventing the technique from reaching its full potential.

UPDATE: Brian mentions in comment this proposal back from 1998 that looked pretty much like this... http://www.w3.org/TR/NOTE-AS

11 Comments

  • I'm a little confused. Can you please comment on why this method is superior to unobtrusive javascript. Doesn't that accomplish your goal: "separate styling, markup *and* behavior" ?

    Thanks

  • @AI: it does the separation but is not declarative. And I'm not saying one is "superior" to the other. They are more like different styles.

  • @Rich: yes, I should have thought about XBL and mentioned it. But is there a pure JavaScript implementation of it that works on all current browsers?
    Also, it is mainly a component model, and the industry hasn't been waiting for that to create purely JavaScript based component models. What I'm advocating here is to physically separate behavior from style (instead of introducing new confusion by merging behavior into style like both htc and xbl are doing). The model that I describe should in principle work with any component model (although I'm using Microsoft Ajax in this example). And the implementation works today on all browsers, while remaining fairly simple.

  • Dojo behavior or the way some jQuery plug-ins work come pretty close, but in an imperative way. Here, I’m playing with a more *declarative* approach that aims at clean separation of style, behavior and markup.

  • I have been waiting something similar to HTC since IE 5, but your idea even better.
    With live behavior instantiation it would be.. so useful

  • @Mario: thanks for the links, DHerman's stuff comes pretty close too. Please note that jQuery Live Query is no longer necessary now that 1.3.1 is out (which is what I'm using in this experiment).

  • Bertrand,

    The only big thing I see is that often times when I am building behaviors (with the MS Ajax) I like to allow for css classes to style various effects. I know this might be difficult but what would be cool is if in those cases the reference could be left out and some kind of mechanism in the CSS stylesheet could clue me in. Something like:

    a $JaysCoolBehavior.ActiveCSSClass {border:solid 1px black;color:red}
    a .someDiv $JaysCoolBehavior.ActiveCSSClass {border:solid 1px black;color:blue}

    presumably my JaysCoolBehavior would use this class specified in place of the property and it would select the appropriate one accordingly.

    I don't know if that's doable or even that what I threw in there is legal... just throwing something else for you to think about.

    Jay Kimble
    -- The Dev Theologian

  • Well,

    In my opinion both, yours and jses solutions, gets pretty close to be good enough.

    Since almost anyone is using jquery nowadays, needing another library just slows down the thing. A jquery plugin based script is better. At that point you're right.

    But, jses linking system for the jses file makes sense, and the aspect of the file is identical to css, and not like json.

    So, a regex based solution, that uses jquery selector and events api, parsing a external file, linked with it's own extension and type, is even better.

    And the funny thing is that is a easy to do regex, since the syntax is always string { string: string }.
    That results on a $(string).live(string, eval(string));

    We could even do type="text/behaviorsheet" to clear out things.

  • It's all good, I would really love this stuff in my browser, but then I have to think of the possible way to abuse or misuse this. How does one debug something like this? How do you do intellisense, if the behaviour file references javascript functions that are declared in js files that have no direct connection to it?
    So, I would press on having a linking syntax first, something that would work either like a "javascript project file" - that can be read by both browser and Visual Studio or whatever IDE is used -, or something like register markup.
    Also, maybe it's just me, but wouldn't it be a lot cleaner if the only thing declared in the CSS is the name of the function to be called on an event fire? I mean, the only possible parameters are 'this' and 'event', and in this way all the javascript would actually be in the js file.


  • I'd like to have a choice whether to instantiate components imperativly via $create, declaratively in markup or declaratively but not touching markup.

  • @Omari: the first two you can do out of the box with Microsoft Ajax 4.0. The behavior sheets I'm showing here enable you to do the third one.

Comments have been disabled for this content.