script.aculo.us

UJS; RJS versus POJS; Prototype Stack versus JQuery Stack

I am accustomed to using Unobtrusive JavaScript (UJS) in my apps. UJS dynamically adds event handlers to the DOM. These event handers implement behaviors via DOM manipulations and XMLHttpRequests (XHR’s). This approach is in contrast to the historical approach of specifying event handlers on HTML elements directly. The value of the UJS approach are:

  • it separates behavior (JavaScript) from markup (HTML) and this is good for the same reasons separating style (CSS) from markup is good
  • it can reduce page weight

I had been using ujs4rails but that thing is being deprecated so it isn’t an option for me. If I want to do UJS I can either use Prototype or JQuery. I don’t know of any other good options.

I’m using JQuery a bit now. Pretty light use: a highlight effect here, a show/hide toggle there; some AJAX form submission. In thinking about how to proceed I’m kind of torn. I don’t want to use both JQuery and Prototype longer term. For starters, that’s too much mental baggage to carry around — one must gain fluency in one or ‘tother I think. Also it’s a lot of page weight to load both libraries.

Another dimension is RJS vs. POJS. RJS is Rails’ templating and API that lets you generate JavaScript from Ruby. POJS is “Plain Old JavaScript”. If we do RJS then we almost have to stay on the Prototype stack. But with POJS a guy could go with either stack and in fact I think the scale tips to the JQuery stack for POJS as I’ll show in a sec.

So here’s how I see the top two options:

RJS: Prototype + Scriptaculous + RJS + Dan Webb’s Lowpro:
+ Lowpro supports UJS
+ most of us have used prototype + scriptaculous and are somewhat comfortable with it
+ native Rails (RJS) support
- Lowpro is not as well documented (nor as widely used) as JQuery for UJS
- I see no centralized library of plugins (other than Scriptaculous itself) for this stack

POJS: JQuery + MinusMOR:
+ it supports UJS out of the box in a pretty clean way
+ people I respect seem to be moving to JQuery
+ there is a large library of plugins including autocompleters and flash upload progress indicators
- very little native Rails (RJS) support — you can do some things but it’s hard to know exactly what will work
- if you already know scriptaculous effects, you have to learn new effects

A big part of this decision hinges on the value of RJS. After using Rails RJS templates for a year and a half or so my conclusion is that they are more trouble than they are worth. Many believe RJS is easier to write/maintain that POJS — especially for a Ruby programmer. I believe RJS is actually a false economy. In real applications you really have to code in real JavaScript.

An RJS template generates JavaScript from Ruby code — but that Ruby API is insufficiently documented (in particular with regard to the scriptaculous effects) and insufficiently capable (e.g. there is no easy way to update DOM elements matching a CSS selector through that API). Essentially a guy can waste a lot of time trying to get RJS to work for anything but the simplest demos. So I lean toward plain-old JavaScript (POJS) instead.

I wonder what you think. Do you place higher value on RJS for real applications? Have you found an alternate stack, or perhaps a different combination of stack elements? Must I pick one stack or the other, or is it possible and profitable to use both at once?

AJAX
RJS Templates
Ruby on Rails
script.aculo.us

Comments (4)

Permalink

Fight id Proliferation and Update Any Element You Want

The Prototype Updater constructor takes an Element (object) or element id (String) as the first parameter. You can see this in prototype.js in Abstract.Insertion.initialize and Ajax.Updater.updateContent . In both situations the first parameter sent to the constructor has $() applied before use. And as I’m sure you’re aware, the effect of that is that if the parameter is an Element, then the same element is returned. OTOH, if the element is a String, then that string is assumed to be an element id and the Element is found in the DOM and returned.

The big deal here is that the documentation lies or at a minimum fails to make this point clearly. Have a look at prototype-api.pdf. And this misunderstanding is carried forward in the Rails wrapper API functionality ActionView::Helpers::PrototypeHelper::JavaScriptGenerator::GeneratorMethods#insert_html, replace_html, remove, show, hide, and visual_effect. The result of this misunderstanding is that many JavaScript programmers think they need id’s all over the place — and Rails programmers actually do need id’s all over the place (unless and until the API is repaired…)

What is wanted in the Ruby wrapper, in order to expose the full capability of the underlying JavaScript libraries, is the ability to pass not only a String (containing an Element id) but optionally to pass an instance of JavaScriptGenerator to insert_html and replace_html and to have that generator rendered. Then we could do (borrowing from the Rails doc and extending…):
    1 update_page do |page|
    2   page.insert_html :bottom, page.select(‘p.welcome b’).first, “<li>#{@item.name}</li>”
    3   page.visual_effect :highlight, ‘list’
    4   page.hide ’status-indicator’, ‘cancel-link’
    5 end

Note that instead of the simple string literal ‘list’ (from the Rails doc) we’ve got a full-fledged expression there. And generate something like this:

    1 new Insertion.Bottom($$(‘p.welcome b’).first, “<li>Some item</li>”);
    2 new Effect.Highlight(‘list’);
    3 [“status-indicator”, “cancel-link”].each(Element.hide);

But really, the proposed change to the Ruby wrapper isn’t limited to CSS selectors of course. Once insert_html, replace_html and friends support a full-fledged generator parameter, you could put arbitrary JavaScript in there. The most obvious examples would be calling custom functions and DOM traversal functions.

AJAX
Ruby on Rails
script.aculo.us

Comments (6)

Permalink

Foolproof AJAX Progress Indicator

spinner Bruce Williams is right on in Avoiding AJAX Faux Pas where he lists four inviolable conditions your AJAX code must meet. The first two conditions address the need to show (then hide) a visual progress indicator to let the user know that a network operation (XHR) is occurring. These are important rules. Unfortunately, the implementation he presents is fraught with difficulties.

Bruce’s solution is comprised of a Ruby function showing_progress which he suggests you use in every single call to (insert_html, replace_html, replace) to wrap any options you’d normally pass to those routines. The showing_progress routine adds :loading and :complete handlers that will show, then hide an element with id ‘progress’ on your page.

The solution is kind of elegant and it works as advertised but I ran into a few problems as I got deeper into my app:

  1. I forgot to call showing_progress in new invocations of insert_html, replace_html, replace — as a result I didn’t get progress indication in those cases
  2. The solution works only for Ruby RJS code — if you write JavaScript that calls Ajax.Request and Ajax.Updater then the solution of course doesn’t help.
  3. The solution ruthlessly overwrites any :loading and :complete handlers you’ve defined in your options

How about a solution that works for both Ruby and JavaScript? It’d be nice if it didn’t rely on the programmer remembering to call it everywhere? It’d be even nicer if it didn’t interfere with your application’s :loading and :complete handlers.

Here’s the solution I’m using. I don’t recall where I first saw it but I’m pretty sure I didn’t invent it. I see Nicky Peeters was suggesting a similar thing over a year ago. Just put this code in application.js:

    1 Ajax.Responders.register({
    2 onCreate: function() {
    3  if($(progress) && Ajax.activeRequestCount>0)
    4  Effect.Appear(progress,{duration:0.5,queue:end});
    5 },
    6
    7 onComplete: function() {
    8  if($(progress) && Ajax.activeRequestCount==0)
    9  Effect.Fade(progress,{duration:0.5,queue:end});
   10 }
   11 });

And stick something like this in your layout:

    1                 <span id=progress style=display:none;>
    2                     <img src=/images/busy.gif>
    3                 </span>

And you’ll have one less worry in your life.

AJAX
RJS Templates
Ruby on Rails
script.aculo.us

Comments (2)

Permalink