Giving jQuery UI another chance (and really liking it)

I feel like I've given jQuery UI a bad rap. I passed judgement too fast. At first glance, it was a pile of rigid prefab user interface objects generated by jQuery being peddled as 'official'. I know now that it's much more.

jQuery UI is not (just) about a fancy accordion slider. It's a framework for organizing, building, and styling UI widgets that can be as simple as a color transition (or as complex as the exercise I used in Bocoup's Advanced jQuery Training, which I'll demonstrate at the end).

The Interactive Core

The easiest pieces to appreciate exist in the interactive tools jQuery UI gives you to make your divs draggable, your lists sortable (and much more). Just include jQuery UI (some or all of it), and initialize the functionality you want on a jQuery selection:

gistfile1.js

// js
$('div#dragme').draggable()

// html
<div id="dragme">
  Drag me! I'm a div
</div>

That's all it takes to allow your visitors to drag & drop objects with the click of a mouse. They've got examples of each of these on their demos page.

Themes

I'm not as keen on themes as some folks are, but I can see their use. If you become a regular consumer of jQuery UI, then themes are a must. In short, jQuery UI applies a set of classes to interface elements (outlined in the jQuery Theme API); If you create CSS that uses these classes, then you've created a theme.

It doesn't stop there. They've taken it a step further, and created an online tool for creating styles that match myriad elements using the theme API. It's called the ThemeRoller, and you should check it out.

Widget Factory

This is the most impressive piece in my opinion. I'm not sure why it gets so little press on the jQuery UI web site (unless jQuery UI is for a different audience than jQuery; I mean, we're all devs, right? Why not tell us about the awesome stuff for devs? I could really care less about the prefab accordion widget ).

The Widget Factory is a way to encapsulate your UI code. Here's how it works:

  • Write a class (include a few Widget Factory-specific methods)
  • Register it within a namespace

Now you have a widget that you can apply to your jQuery collections just like .draggable()! Daniel Wachsstock wrote a gorgeous blog post about using the Widget Factory called “Understanding jQuery UI widgets: A Tutorial”. It was game-changing for me.

As I mentioned earlier, I created a demo of the Widget Factory in action for Bocoup's Advanced jQuery Training (in progress as of this writing). Let's dive into that now:

To start, I created an HTML form, with sections broken down into fieldsets. I gave the form an ID, and not much else. It's plain, and it's functional, as you can see:

gistfile1.html

<form id="playform">

  <fieldset>
    <legend>Contact Info:</legend>
    Name: <input type="text" size="30" /><br />
    Email: <input type="text" size="30" /><br />
    Date of birth: <input type="text" size="10" />
  </fieldset>

  <fieldset>
    <legend>Computer Preferences:</legend>
    OS type: <input type="radio" name="osx" /> OSX &amp;mdash; <input type="radio" name="Win" /> Windows &amp;mdash; <input type="radio" name="unix" /> Unix<br/>
    Computer name: <input type="text" size="30" /><br />
    Computer type: <input type="radio" name="laptop" /> Laptop &amp;mdash; <input type="radio" name="Desktop" /> Desktop
  </fieldset>

  <fieldset>
    <legend>Clothing Style:</legend>
    $ spent per month: <input type="text" size="10" /><br />
    Self-evaluation: <input type="radio" name="grungy" /> Grungy &amp;mdash; <input type="radio" name="nerdy" /> Nerdy &amp;mdash; <input type="radio" name="refined" /> Refined<br/>
  </fieldset>

  <fieldset>
    <legend>Dining Options:</legend>
    Chicken or Fish? <input type="radio" name="chix" /> Chicken &amp;mdash; <input type="radio" name="Fish" /> Fish<br/>
  </fieldset>

        <input type="submit" value="submit my information">

</form>

Then I created a “class” for the Widget Factory. I called it ProgressBar, and it's purpose is to transform this plain HTML form into a multipart form enhanced by a progress bar that changes as you proceed through the parts. Here's my JavaScript (jQuery and jQuery UI are required):

gistfile1.js

var ProgressForm = {

        _currentStep: 1,
        _totalSteps: 0,

        _prevButton: false,
        _nextButton: false,
        _progressbar: false,

        // this would be _init() for jQuery UI versions < 1.8
        _create: function() {
                this.element.find('fieldset:not(:first)').hide();
                this.element.find('input[type=submit]').css('float','right').hide();

                this._totalSteps = this.element.find('fieldset').length;

                this._prevButton = $('<input type="button" value="previous step">');
                this._nextButton = $('<input type="button" value="next step">');

                this.element.append(this._prevButton).append(' ').append(this._nextButton);
                this._prevButton.hide();

                this._progressbar = $('<div id="progress"/>').prependTo(this.element);
                this._progressbar.progressbar({ value: ((100/this._totalSteps) * this._currentStep) });

                this._progressbar.find('.ui-progressbar-value').text('Step ' + this._currentStep + ' of '+ this._totalSteps).css('text-align','right').css('padding','0.5em 0.5em 0 0');

                var tmp = this;
                this._nextButton.click(function() { tmp.nextStep(); });
                this._prevButton.click(function() { tmp.prevStep(); });
        },

        _update_state: function() {
                this._prevButton.toggle(this._currentStep > 1);
                this._nextButton.toggle(this._currentStep < this._totalSteps);
                this.element.find('input[type=submit]').toggle(this._currentStep == this._totalSteps);
                this._progressbar.progressbar("option", "value", ((100/this._totalSteps) * this._currentStep));
                this._progressbar.find('.ui-progressbar-value').text('Step ' + this._currentStep + ' of '+ this._totalSteps);
        },

        nextStep: function () {
                this.element.find('fieldset:visible').hide().next().show();
                this._currentStep += 1;
                this._update_state();
        },
        prevStep: function() {
                this.element.find('fieldset:visible').hide().prev().show();
                this._currentStep -= 1;
                this._update_state();
        }
};


$(function() {

        // register the widget
        $.widget('bocoup.progressform', ProgressForm);

        // create the widget on the form
        $('#playform').progressform();

});

My ProgressBar class look complicated, I know. So let me explain what's going on:

  • The ._init() method writes some HTML to the DOM and add some event listeners to 'next' and 'prev' buttons
  • The ._init() method ALSO initializes a prefab jQuery UI widget called progressbar
  • The nextStep & prevStep methods change the state of the widget & trigger update methods
  • The rest keeps track of what should be visible, and what shouldn't

After the widget code (when the document is done loading) I register the ProgressBar class as a widget with $.widget() under the 'bocoup.progressbar' namespace. This extends a widget class that lives under the hood with the class you wrote, and attaches a reference to your jQuery collection based on the namespace you provided. It sounds complicated, but look at the code again. It allows us to quickly match the conventions set by jQuery for extending elements.

Here's a quick screencast. The screencast starts with my widget disabled to demonstrate what the form looks like in it's default state. After a few seconds, I refresh the page and demonstrate the effect that my widget has on the form.

Pretty cool, am I right? I hope you give it another look, and find something you like.

Comments

Contact Us

We'd love to hear from you. Get in touch!

Boston

201 South Street, Boston, MA 02111

New York

315 Church St, New York, NY 10013

Phone & Email

(617)379-2752 hello@bocoup.com