Chapter 11. jQuery UI widgets: Beyond HTML controls

This chapter covers

  • Extending the set of HTML controls with jQuery UI widgets
  • Augmenting HTML buttons
  • Using slider and datepicker controls for numeric and date input
  • Showing progress visually
  • Simplifying long lists with autocompleters
  • Organizing content with tabs and accordions
  • Creating dialog boxes

Since the dawn of the web, developers have been constrained by the limited set of controls afforded by HTML. Although that set of controls runs the gamut from simple text entry through complex file selection, the variety of provided controls pales in comparison to those available to desktop application developers. HTML 5 promises to expand this set of controls, but it may be some time before support appears in all major browsers.

For example, how often have you heard the HTML <select> element referred to as a “combo box,” a desktop control to which it bears only a passing resem blance? The real combo box is a very useful control that appears often in desktop applications, yet web developers have been denied its advantages.

But as computers have become more powerful, browsers have increased their capabilities, and DOM manipulation has become a commonplace activity, clever web developers have been taking up the slack. By creating extended controls—either augmenting the existing HTML controls or creating controls from scratch using basic elements—the developer community has shown nothing short of sheer ingenuity in using the tools at hand to make the proverbial lemonade from lemons.

Standing on the shoulders of core jQuery, jQuery UI brings this ingenuity to us, as jQuery users, by providing a set of custom controls to solve common input problems that have traditionally been difficult to solve using the basic control set. Be it making standard elements play well (and look good) in concert with other elements, accepting numeric values within a range, allowing the specification of date values, or giving us new ways to organize our content, jQuery UI offers a valuable set of widgets that we can use on our pages to make data entry a much more pleasurable experience for our users (all while making it easier on us as well).

Following our discussion of the core interactions provided by jQuery UI, we’ll continue our exploration by seeing how jQuery UI fills in some gaps that the HTML control set leaves by providing custom controls (widgets) that give us more options for accepting user input. In this chapter, we’ll explore the following jQuery UI widgets:

Like the previous chapter, this is a long one! And as with interactions, the jQuery UI methods that create widgets follow a distinct pattern that makes them easy to understand. But unlike interactions, the widgets pretty much stand on their own, so you can choose to skip around the sections in this chapter in any order you like.

We’ll start with one of the simpler widgets that lets us modify the style of existing control elements: buttons.

11.1. Buttons and buttonsets

At the same time that we lament the lack of variety in the set of HTML 4 controls, it offers a great number of button controls, many of which overlap in function.

There’s the <button> element, and no less than six varieties of the <input> element that sport button semantics: button, submit, reset, image, checkbox, and radio. Moreover, the <button> element has subtypes of button, submit, and reset, whose semantics overlap those of the corresponding input element types.

 

Note

Why are there so many HTML button types? Originally, only the <input> button types were available, but as they could only be defined with a simple text string, they were deemed limiting. The <button> element was added later; it can contain other elements and thereby offers more rendering possibilities. The simpler <input> varieties were never deprecated, so we’ve ended up with the plethora of overlapping button types.

 

All these buttons types offer varying semantics, and they’re very useful within our pages. But, as we’ll see when we explore more of the jQuery UI widget set, their default visual style may not blend well with the styles that the various widgets exhibit.

11.1.1. Button appearance within UI themes

Remember back when we downloaded jQuery UI near the beginning of chapter 9? We were given a choice of various themes to download, each of which applies a different look to the jQuery UI elements.

To make our buttons match these styles, we could poke around the CSS file for the chosen theme and try to find styles that we could apply to the button elements to bring them more into line with how the other elements look. But as it turns out, we don’t have to—jQuery UI provides a means to augment our button controls so their appearance matches the theme without changing the semantics of the elements. Moreover, it will also give them hover styles that will change their appearance slightly when the mouse pointer hovers over them—something the unstyled buttons lack.

The button() method will modify individual buttons to augment their appearance, while the buttonset() method will act upon a set of buttons (most often a set of radio buttons or checkboxes) not only to theme them, but to make them appear as a cohesive unit.

Consider the display in figure 11.1.

Figure 11.1. Various button elements without any styling—rather boring, wouldn’t you say?

This page fragment shows the unthemed display of some individual button elements, and some groupings of checkboxes, radio buttons, and <button> elements. All perfectly functional, but not exactly visually exciting.

After applying the button() method to the individual buttons, and the buttonset() method to the button groups (in a page using the Cupertino theme), the display changes to that shown in figure 11.2.

Figure 11.2. After a style makeover, our buttons are dressed in their best and ready to hit the town!

After styling, the new buttons make those shown in figure 11.1 look positively Spartan.

Not only has the appearance of the buttons been altered to match the theme, the groups have been styled so that the buttons in the group form a visual unit to match their logical grouping. And even though the radio buttons and checkboxes have been restyled to look like “normal” buttons, they still retain their semantic behaviors. We’ll see that in action when we introduce the jQuery UI Buttons Lab.

This theme’s styling is one we’ll become very familiar with as we progress through the jQuery UI widgets in the remainder of this chapter.

But first we’ll take a look at the methods that apply this styling to the button elements.

11.1.2. Creating themed buttons

The methods that jQuery UI provides to create widgets follow the same style we saw in the previous chapter for the interaction methods: calling the button() method and passing an options hash creates the widget in the first place, and calling the same method again but passing a string that identifies a widget-targeted operation modifies the widget.

The syntax for the button() and buttonset() methods is similar to the methods we investigated for the UI interactions:

 

Command syntax: button and buttonset
button(options)button('disable')button('enable')button('destroy')button('option',optionName,value)buttonset(options)buttonset('disable')buttonset('enable')buttonset('destroy')buttonset('option',optionName,value)

Themes the elements in the wrapped set to match the currently loaded jQuery UI theme. Button appearance and semantics will be applied even to non-button element such as <span> and <div>.

Parameters

options

(Object) An object hash of the options to be applied to the elements in the wrapped set, as described in table 11.1, making them themed buttons.

'disable'

(String) Disables click events for the elements in the wrapped set.

'enable'

(String) Re-enables button semantics for the elements in the wrapped set.

'destroy'

(String) Reverts the elements to their original state, before applying the UI theme.

'option'

(String) Allows option values to be set on all elements of the wrapped set, or to be retrieved from the first element of the wrapped set (which should be a jQuery UI button element), based upon the remaining parameters. If specified, at least the optionName parameter must also be provided.

optionName

(String) The name of the option (see table 11.1) whose value is to be set or returned. If a value parameter is provided, that value becomes the option’s value. If no value parameter is provided, the named option’s value is returned.

value

(Object) The value to be set for the option identified by the optionName parameter.

Returns

The wrapped set, except for the case where an option value is returned.

 

To apply button theming to a set of elements, we call the button() or buttonset() method with a set of options, or with no parameter to accept the default options. Here’s an example:

$(':button').button({ text: true });

 

Note

The word “theming” isn’t in the dictionary, but it’s what jQuery UI uses, so we’re running with it.

 

The options that are available to use when creating buttons are shown in table 11.1.

The button() method comes with plenty of options, and you can try them out in the Buttons Lab page, which you’ll find in chapter11/buttons/lab.buttons.html and is shown in figure 11.3.

Figure 11.3. The jQuery UI Buttons Lab page lets us see the before and after states of buttons, as well as fiddle with the options.

Follow along in this Lab as you read through the options list in table 11.1.

Table 11.1. Options for the jQuery UI buttons and buttonsets

Option

Description

In Lab?

icons

(Object) Specifies that one or two icons are to be displayed in the button: primary icons to the left, secondary icons to the right. The primary icon is identified by the primary property of the object, and the secondary icon is identified by the secondary property.

The values for these properties must be one of the 174 supported call names that correspond to the jQuery button icon set. We’ll discuss these in a moment.

If omitted, no icons are displayed.

label

(String) Specifies text to display on the button that overrides the natural label. If omitted, the natural label for the element is displayed. In the case of radio buttons and checkboxes, the natural label is the <label> element associated with the control.

text

(Boolean) Specifies whether text is to be displayed on the button. If specified as false, text is suppressed if (and only if) the icons option specifies at least one icon. By default, text is displayed.

These options are straightforward except for the icons options. Let’s chat a little about that.

11.1.3. Button icons

jQuery UI supplies a set of 174 themed icons that can be displayed on buttons. You can show a single icon on the left (the primary icon), or one on the left and one on the right (as a secondary icon).

Icons are specified as a class name that identifies the icon. For example, to create a button with an icon that represents a little wrench, we’d use this code:

$('#wrenchButton').button({  icons: { primary: 'ui-icon-wrench' }});

If we wanted a star on the left, and a heart on the right, we’d do this:

$('#weirdButton').button({  icons: { primary: 'ui-icon-star', secondary: 'ui-icon-heart' }});

Because we all know how many words a picture is worth, rather than just listing the available icon names here, we’ve provided a page that creates a button for each of the icons, labeled with the name of the icon. You’ll find this page at chapter11/buttons/ ui-button-icons.html, and it’s shown in figure 11.4.

Figure 11.4. The jQuery UI Button Icons page lets us see all the available button icons along with their names.

You might want to keep this page handy for whenever you want to find an icon to use on your buttons.

11.1.4. Button events

Unlike the interactions and the remainder of the widgets, there are no custom events associated with jQuery UI buttons.

Because these widgets are merely themed versions of existing HTML 4 controls, the native events can be used as if the buttons had not been augmented. To handle button clicks, we simply continue to handle click events for the buttons.

11.1.5. Styling buttons

The whole purpose of using the jQuery UI button() and buttonset() methods is to make the buttons match the chosen jQuery UI theme. It’s as if we took the buttons and sent them to appear on What Not to Wear (a popular US and UK TV makeover reality show); they start out drab and homely and emerge looking fabulous! But even so, we may want to fine-tune those styled elements to make them work better on our pages. For example, the text of the buttons on the Buttons Icon page was made smaller to fit the buttons on the page.

jQuery UI augments or creates new elements when creating widgets, and it applies class names to the elements that match the style rules in the theme’s CSS style sheet. We can use these class names ourselves to augment or override the theme definitions on our pages.

For example, in the Button Icons page, the button text’s font size was adjusted like this:

.ui-button-text { font-size: 0.8em; }

The class name ui-button-text is applied to the <span> element that contains the button text.

It would be nearly impossible to cover all the permutations of elements, options, and class names for the widgets created by jQuery UI, so we’re not even going to try. Rather, the approach that we’ll take is to provide, for each widget type, some tips on some of the most common styling that we’re likely to need on our pages. The previous tip on restyling the button text is a good example.

Button controls are great for initiating actions, but except for radio buttons and checkboxes, they don’t represent values that we might want to obtain from the user. A number of the jQuery UI widgets represent logical form controls that make it easy for us to obtain input types that have long been an exercise in pain. Let’s take a look at one that eases the burden of obtaining numeric input.

11.2. Sliders

Numeric input has traditionally been a thorn in the side of web developers everywhere. The HTML 4 control set just doesn’t have a control that’s well suited to accepting numeric input.

A text field can be (and is most often) used to accept numeric input. This is less than optimal because the value must be converted and validated to make sure that the user doesn’t enter “xyz” for their age or for the number of years they’ve been at their residence.

Although after-the-fact validation isn’t the greatest of user experiences, filtering the input to the text control such that only digits can be entered has its own issues. Users might be confused when they keep hitting the A key and nothing happens.

In desktop applications, a control called a slider is often used whenever a numeric value within a certain range is to be obtained. The advantage of a slider over text input is that it becomes impossible for the user to enter a bad value. Any value that they can pick with the slider is valid.

jQuery UI brings us a slider control so that we can share that advantage.

11.2.1. Creating slider widgets

A slider generally takes the form of a “trough” that contains a handle. The handle can be moved along the trough to indicate the value selected within the range, or the user can click within the trough to indicate where the handle should move to within the range.

Sliders can be arranged either horizontally or vertically. Figure 11.5 shows an example of a horizontal slider from a desktop application.

Figure 11.5. Sliders can be used to represent a range of values; in this example, from minimum to full brightness.

Unlike the button() method, sliders aren’t created by augmenting an existing HTML control. Rather, they’re composited from basic elements like <div> and <a>. The target <div> element is styled to form the trough of the slider, and anchor elements are created within it to form the handles.

The slider widget can possess any number of handles and can therefore represent any number of values. Values are specified using an array, with one entry for each handle. However, as the single-handle case is so much more common than the multi-handle case, there are methods and options that treat the slider as if it had a single value.

This prevents us from having to deal with arrays of a single element for the way that we’ll use sliders most often. Thanks jQuery UI team! We appreciate it!

This is the method syntax for the slider() method:

 

Command syntax: slider
slider(options)slider('disable')slider('enable')slider('destroy')slider('option',optionName,value)slider('value',value)slider('values',index,values)

Transforms the target elements (<div> elements recommended) into a slider control.

Parameters

options

(Object) An object hash of the options to be applied to the elements in the wrapped set, as described in table 11.2 making them sliders.

'disable'

(String) Disables slider controls.

'enable'

(String) Re-enables disabled slider controls.

'destroy'

(String) Reverts any elements transformed into slider controls to their previous state.

'option'

(String) Allows option values to be set on all elements of the wrapped set, or to be retrieved from the first element of the wrapped set (which should be a slider element), based upon the remaining parameters. If specified, at least the optionName parameter must also be provided.

optionName

(String) The name of the option (see table 11.2) whose value is to be set or returned. If a value parameter is provided, that value becomes the option’s value. If no value parameter is provided, the named option’s value is returned.

value

(Object) The value to be set for the option identified by the optionName parameter (when used with 'option'), the value to be set for the slider (if used with 'value'), or the value to be set for the handles (if used with 'values').

'value'

(String) If value is provided, sets that value for the single-handle slider and returns that value; otherwise the slider’s current value is returned.

'values'

(String) For sliders with multiple handles, gets or sets the value for specific handles, where the index parameter must be specified to identify the handles. If the values parameter is provided, sets the value for the handles. The values of the specified handles are returned.

index

(Number|Array) The index, or array of indexes, of the handles to which new values are to be assigned.

Returns

The wrapped set, except for the case where an option or handle value is returned.

 

When creating a slider, there are a good variety of options for creating slider controls with various behaviors and appearance.

While you’re reading through the options in table 11.2, bring up the Sliders Lab in file chapter11/sliders/lab.sliders.html (shown in figure 11.6), and follow along, trying out the various options.

Table 11.2. Options for the jQuery UI sliders

Option

Description

In Lab?

animate

(Boolean|String|Number) If true, causes the handle to move smoothly to a position clicked to within the trough. Can also be a duration value or one of the strings slow, normal, or fast. By default, the handle is moved instantaneously.

change

(Function) Specifies a function to be established on the slider as an event handler for slidechange events. See the description of the slider events in table 11.3 for details on the information passed to this handler.

max

(Number) Specifies the upper value of the range that the slider can attain—the value represented when the handle is moved to the far right (for horizontal sliders) or top (for vertical sliders). By default, the maximum value of the range is 100.

min

(Number) Specifies the lower value of the range that the slider can attain—the value represented when the handle is moved to the far left (for horizontal sliders) or bottom (for vertical sliders). By default, the minimum value of the range is 0.

orientation

(String) One of horizontal or vertical. Defaults to horizontal.

range

(Boolean|String) If specified as true, and the slider has exactly two handles, an element that can be styled is created between the handles. If the slider has a single handle, specifying min or max creates a range element from the handle to the beginning or end of the slider respectively. By default, no range element is created.

start

(Function) Specifies a function to be established on the sliders as an event handler for slidestart events. See the description of the slider events in table 11.3 for details on the information passed to this handler.

slide

(Function) Specifies a function to be established on the slider as an event handler for slide events. See the description of the slider events in table 11.3 for details on the information passed to this handler.

step

(Number) Specifies discrete intervals between the minimum and maximum values that the slider is allowed to represent. For example, a step value of 2 would allow only even numbers to be selected.

The step value should evenly divide the range.

By default, step is 1 so that all values can be selected.

stop

(Function) Specifies a function to be established on the slider as an event handler for slidestop events. See the description of the slider events in table 11.3 for details on the information passed to this handler.

value

(Number) Specifies the initial value of a single-handle slider. If there are multiple handles (see the values options), specifies the value for the first handle.

If omitted, the initial value is the minimum value of the slider.

values

(Array) Causes multiple handles to be created and specifies the initial values for those handles. This option should be an array of possible values, one for each handle.

For example, [10,20,30] will cause the slider to have three handles with initial values of 10, 20, and 30.

If omitted, only a single handle is created.

Figure 11.6. The jQuery UI Sliders Lab shows the various ways that jQuery UI sliders can be set up and manipulated.

Now let’s explore the events that slider controls can trigger.

11.2.2. Slider events

As with the interactions, most of the jQuery UI widgets trigger custom events when interesting things happen to them. We can establish handlers for these events in one of two ways. We can bind handlers in the customary fashion at any point in the ancestor hierarchy, or we can specify the handler as an option, which is what we saw in the previous section.

For example, we might want to handle sliders’ slide events in a global fashion on the body element:

$('body').bind('slide',function(event,info){ ... });

This allows us to handle slide events for all sliders on the page using a single handler. If the handler is specific to an instance of a slider, we might use the slide option instead when we create the slider:

$('#slider').slider({ slide: function(event,info){ ... } });

This flexibility allows us to establish handlers in the way that best suits our pages.

As with the interaction events, each event handler is passed two parameters: the event instance, and a custom object containing information about the control. Isn’t consistency wonderful?

The custom object contains the following properties:

  • handle—A reference to the <a> element for the handle that’s been moved.
  • value—The current value represented by the handle being moved. For single-handle sliders, this is considered the value of the slider.
  • values—An array of the current values of all sliders; this is only present for multi-handled sliders.

In the Sliders Lab, the value and values properties are used to keep the value display below the slider up to date.

The events that sliders can trigger are summarized in table 11.3.

Table 11.3. Events for the jQuery UI sliders

Event

Option

Description

slide

slide

Triggered for mousemove events whenever the handle is being dragged through the trough. Returning false cancels the slide.

slidechange

change

Triggered whenever a handle’s value changes, either through user action or programmatically.

slidestart

start

Triggered when a slide starts.

slidestop

stop

Triggered when a slide stops.

The slidechange event is likely to be the one of most interest because it can be used to keep track of the slider’s value or values.

Let’s say that we have a single-handled slider whose value needs to be submitted to the server upon form submission. Let’s also suppose that a hidden input with a name of sliderValue is to be kept up to date with the slide value so that when the enclosing form is submitted, the slider’s value acts like just another form control. We could establish an event on the form as follows:

$('form').bind('slidechange',function(event,info){  $('[name="sliderValue"]').val(info.value);});

Here’s a quick exercise for you to tackle:

  • Exercise 1The preceding code is fine as long as there is only one slider in the form. Change the preceding code so that it can work for multiple sliders. How would you identify which hidden input element corresponds to the individual slider controls?

Now let’s add some style to our sliders.

11.2.3. Styling tips for sliders

When an element is transformed into a slider, the class ui-slider is added to it. Within this element, <a> elements will be created to represent the handles, each of which will be given the ui-slider-handle class. We can use these class names to augment the styles of these elements as we choose.

 

Tip

Can you guess why anchor elements are used to represent the handles? Time’s up—it’s so that the handles are focusable elements. In the Sliders Lab, create a slider and set focus to a handle by clicking upon it. Now use the left and right arrow keys and see what happens.

 

Another class that will be added to the slider element is either ui-slider-horizontal or ui-slider-vertical, depending upon the orientation of the slider. This is a useful hook we can use to adjust the style of the slider based upon orientation. In the Sliders Lab, for example, you’ll find the following style rules, which adjust the dimensions of the slider as appropriate to its orientation:

.testSubject.ui-slider-horizontal {  width: 320px;  height: 8px;}.testSubject.ui-slider-vertical {  height: 108px;  width: 8px;}

The class name testSubject is the class that’s used within the Lab to identify the element to be transformed into the slider.

Here’s another neat tip: let’s suppose that in order to match the rest of our site, we’d like the slider handler to look like a fleur-de-lis. With an appropriate image and a little CSS magic, we can make that happen.

In the Sliders Lab, reset everything, check the checkbox labeled Use Image Handle, and click Apply. The slider looks as shown in figure 11.7.

Figure 11.7. With a PNG image and a little CSS magic, we can make the slider handle look like whatever we want.

Here’s how it was done. First, a PNG image with a transparent background and containing the fleur-de-lis was created, named handle.png. 18 by 18 pixels seems like a good size. Then the following style rule was added to the page:

.testSubject a.ui-slider-handle.fancy {  background: transparent url('handle.png') no-repeat 0 0;  border-width: 0;}

Finally, after the slider was created, the fancy class was added to the handle.

$('.testSubject .ui-slider-handle').addClass('fancy'),

One last tip: if you create a range element via the range option, you can style it using the ui-widget-header class. We do so in the Lab page with this line:

.ui-slider .ui-widget-header { background-color: orange; }

Sliders are a great way to let users enter numeric values in a range without a lot of aggravation on our part or the user’s. Let’s take a look at another widget that can help us keep our users happy.

11.3. Progress bars

Little irks a user more than sitting through a long operation without knowing whether anything is really happening behind the scenes. Although users are somewhat more accustomed to waiting for things in web applications than in desktop applications, giving them feedback that their data is actually being processed makes for much happier, less anxious users.

It’s also beneficial to our applications. Nothing good can come of a frustrated user clicking away on our interface and yelling, “Where’s my data!” at the screen. The flurry of resulting requests will at best help to bog down our servers, and at worst can cause problems for the backend code.

When a fairly accurate and deterministic means of determining the completion percentage of a lengthy operation is available, a progress bar is a great way to give the user feedback that something is happening.

Visually, a progress bar generally takes the form of a rectangle that gradually “fills” from left to right with a visually distinct inner rectangle to indicate the completion percentage of an operation. Figure 11.8 shows an example progress bar depicting an operation that’s a bit less than half complete.

Figure 11.8. A progress bar shows the completion percentage of an operation by “filling” the control from left to right.

jQuery UI provides an easy-to-use progress bar widget that we can use to let users know that our application is hard at work performing the requested operation. Let’s see just how easy it is to use.

 

When not to use progress bars

Even worse than making the user guess when an operation will complete is lying to them about it.

Progress bars should only be used when a reasonable level of accuracy is possible. It’s never a good idea to have a progress bar that reaches 10 percent and suddenly jumps to the end (leading users to believe that the operation may have aborted in midstream), or even worse, to be pegged at 100 percent long before the operation actually completes.

If you can’t determine an accurate completion percentage, a good alternative to a progress bar is just some indication that something might take a long time; perhaps a text display along the lines of “Please wait while your data is processed—this may take a few minutes ...”, or perhaps an animation that gives the illusion of activity while the lengthy operation progresses.

For the latter, a handy website at http://www.ajaxload.info/ generates GIF animations that you can tailor to match your theme.

 

11.3.1. Creating progress bars

Not surprisingly, progress bars are created using the progressbar() method, which follows the same pattern that’s become so familiar:

 

Command syntax: progressbar
progressbar(options)progressbar('disable')progressbar('enable')progressbar('destroy')progressbar('option',optionName,value)progressbar('value',value)

Transforms the wrapped elements (<div> elements recommended) into a progress bar widget.

Parameters

options

(Object) An object hash of the options to be applied to the created progress bars, as described in table 11.4.

'disable'

(String) Disables a progress bar.

'enable'

(String) Re-enables a disabled progress bar.

'destroy'

(String) Reverts the elements made into progress bar widgets to their original state.

'option'

(String) Allows option values to be set on all elements of the wrapped set, or to be retrieved from the first element of the wrapped set (which should be a progress bar element), based upon the remaining parameters. If specified, at least the optionName parameter must also be provided.

optionName

(String) The name of the option (see table 11.4) whose value is to be set or returned. If a value parameter is provided, that value becomes the option’s value. If no value parameter is provided, the named option’s value is returned.

value

(String|Number) The value to be set for the option identified by the optionName parameter (when used with 'option'), or the value between 0 and 100 to be set for the progress bar (if used with 'value').

'value'

(String) If value is provided, sets that value for the progress bar; otherwise the progress bar’s current value is returned.

Returns

The wrapped set, except for the case where a value is returned.

 

Progress bars are conceptually simple widgets, and this simplicity is reflected in the list of options available for the progressbar() method. There are only two, as shown in table 11.4.

Table 11.4. Options for the jQuery UI progress bars

Option

Description

change

(Function) Specifies a function to be established on the progress bar as an event handler for progressbarchange events. See the description of the progress bar events in table 11.5 for details on the information passed to this handler.

value

(Number) Specifies the initial value of the progress bar; 0, if omitted.

Once a progress bar is created, updating its value is as easy as calling the value variant of the method:

$('#myProgressbar').progressbar('value',75);

Attempting to set a value greater than 100 will result in the value being set to 100. Similarly, attempting to set a value that’s a negative number will result in a value of 0.

The options are simple enough, as are the events defined for progress bars.

11.3.2. Progress bar events

The single event defined for progress bars is shown in table 11.5.

Table 11.5. Events for the jQuery UI progress bars

Event

Option

Description

progressbarchange

change

Called whenever the value of the progress bar changes. Two parameters are passed: the event instance, and an empty object. The latter is passed in order to be consistent with the other jQuery UI events, but no information is contained within the object.

Catching the progressbarchange event could be useful in updating a text value on the page that shows the exact completion percentage of the control, or for any other reason that the page might need to know when the value changes.

The progress bar is so simple—only two options and one event—that a Lab page for this control has not been provided. Rather, we thought we’d create a plugin that automatically updates a progress bar as a lengthy operation progresses.

11.3.3. An auto-updating progress bar plugin

When we fire off an Ajax request that’s likely to take longer to process than a normal person’s patience will accept, and we know that we can deterministically obtain the completion percentage, it’s a good idea to comfort the user by displaying a progress bar.

Let’s think of the steps we’d run through to accomplish this:

1.  

Fire off the lengthy Ajax operation.

2.  

Create a progress bar with a default value of 0.

3.  

At regular intervals, fire off additional requests that take the pulse of the lengthy operation and return the completion status. It’s imperative that this operation be quick and accurate.

4.  

Use the result to update the progress bar and any text display of the completion percentage.

Sounds pretty easy, but there are a few nuances to take into account, such as making sure that the interval timer is destroyed at the right time.

Defining the Auto-Progressbar Widget

As this widget is something that could be generally useful across many pages, and because there are non-trivial details to take into account, creating a plugin that’s going to handle this for us sounds like a great idea.

We call our plugin the auto-progressbar, and its method, autoProgressbar(), is defined as follows:

 

Command syntax: autoProgressbar
autoProgressbar(options)autoProgressbar('stop')autoProgressbar('destroy')autoProgressbar('value',value)

Transforms the wrapped elements (<div> elements recommended) into a progress bar widget.

Parameters

options

(Object) An object hash of the options to be applied to the created progress bars, as described in table 11.6.

'stop'

(String) Stops the auto-progressbar widget from checking the completion status.

'destroy'

(String) Stops the auto-progressbar widget and reverts the elements made into the progress bar widget to their original state.

'value'

(String) If value is provided, sets that value for the progress bar; otherwise the progress bar’s current value is returned.

value

(String|Number) A value between 0 and 100 to be set for the progress bar used with the 'value' method.

Returns

The wrapped set, except for the case where a value is returned.

 

The options that we’ll define for our plugin are shown in table 11.6.

Table 11.6. Options for the autoProgressbar() plugin method

Option

Description

pulseUrl

(String) Specifies the URL of a server-side resource that will check the pulse of the backend operation that we want to monitor. If this option is omitted, the method performs no operation.

The response from this resource must consist of a numeric value in the range of 0 through 100 indicating the completion percentage of the monitored operation.

pulseData

(Object) Any data that should be passed to the resource identified by the pulseUrl option. If omitted, no data is sent.

interval

(Number) The duration, in milliseconds, between pulse checks. The default is 1000 (1 second).

change

(Function) A function to be established as the progressbarchange event handler.

Let’s get to it.

Creating the Auto-Progressbar

As usual, we’ll start with a skeleton for the method that follows the rules and practices we laid out in chapter 7. (Review chapter 7 if the following doesn’t seem familiar.) In a file named jquery.jqia2.autoprogressbar.js we write this outline:

(function($){  $.fn.autoProgressbar = function(settings,value) {//implementation will go here    return this;  };})(jQuery);

The first thing we’ll want to do is check to see if the first parameter is a string or not. If it’s a string, we’ll use the string to determine which method to process. If it’s not a string, we’ll assume it’s an options hash. So we add the following conditional construct:

if (typeof settings === "string") {  // process methods here}else { // process options here}

Because processing the options is the meat of our plugin, we’ll start by tackling the else part. First, we’ll merge the user-supplied options with the set of default options, as follows:

settings = $.extend({  pulseUrl: null,  pulseData: null,  interval: 1000,  change: null},settings||{});if (settings.pulseUrl == null) return this;

As in previous plugins that we’ve developed, we use the $.extend() function to merge the objects. Note also that we continue with the practice of listing all options in the default hash, even if they have a null value. This makes for a nice place to see all the options that the plugin supports.

After the merge, if the pulseUrl option hasn’t been specified, we return, performing no operation—if we don’t know how to contact the server, there’s not much we can do.

Now it’s time to actually create the progress bar widget:

this.progressbar({value:0,change:settings.change});

Remember, within a plugin, this is a reference to the wrapped set. We call the jQuery UI progress bar method on this set, specifying an initial value of 0, and passing on any change handler that the user supplied.

Now comes the interesting part. For each element in the wrapped set (chances are there will only be one, but why limit ourselves?) we want to start an interval timer that will check the status of the lengthy operation using the supplied pulseUrl. Here’s the code we use for that:

There’s a lot going on here, so let’s take it one step at a time.

We want each progress bar that will be created to have its own interval timer. Why a user would want to create multiple auto-progressbars may be beyond us, but it’s the jQuery way to let them have their rope. We use the each() method to deal with each wrapped element separately.

For both readability, as well as for use within closures that we’ll later create, we capture the wrapped element in the bar$ variable.

We then want to start the interval timer, but we need to keep in mind that later on we’re going to want to stop the timer. So we need to store the handle that identifies the timer somewhere that we can easily get at later. jQuery’s data() method comes in handy for this , and we use it to store the handle on the bar element with a name of autoProgressbar-interval.

A call to JavaScript’s window.setInterval() function starts the timer . To this function we pass an inline function that we want to execute on every tick of the timer, and the interval value that we obtain from the interval option.

Within the timer callback, we fire off an Ajax request to the URL supplied by the pulseUrl option, with any data supplied via pulseData. We also turn off global events (these requests are happening behind the scenes, and we don’t want to confuse the page by triggering global Ajax events that it should know nothing about), and specify that we’ll be getting JSON data back as the response.

Finally, in the success callback for the request , we update the progress bar with the completion percentage (which was returned as the response and passed to the callback). If the value has reached 100, indicating that the operation has completed, we stop the timer by calling our own stop method.

After that, implementing the remaining methods will seem easy. In the if part of the high-level conditional statement (the one that checked to see if the first parameter was a string or not), we write this:

In this code fragment, we switch to different processing algorithms based on the string in the settings parameter , which should contain one of: stop, value, or destroy.

For stop we want to kill off all the interval timers that we created for the elements in the wrapped set . We retrieve the timer handle, which we conveniently stored as data on the element, and pass it to the window.clearInterval() method to stop the timer.

If the method was specified as value, we simply pass the value along to the value method of the progress bar widget.

When destroy is specified, we want to stop the timer, so we just call our own stop method (why copy and paste the same code twice?), and then we destroy the progress bar.

And we’re done! Note how whenever we return from any call to our method, we return the wrapped set so that our plugin can participate in jQuery chaining just like any other chainable method.

The full implementation of this plugin can be found in file chapter11/progressbars/jquery.jqia2.autoprogressbar.js.

Let’s now turn our attention to testing our plugin.

Testing the Auto-Progressbar Plugin

The file chapter11/progressbars/autoprogressbar-test.html contains a test page that uses our new plugin to monitor the completion progress of a long-running Ajax operation. In the interest of saving some space, we won’t examine every line of code in that file, but we will concentrated on the portions relevant to using our plugin.

First, let’s look at the markup that creates the DOM structures of note:

<div>  <button type="button" id="startButton" class="green90x24">Start</button>  (starts a lengthy operation)</div><div>  <div id="progressBar"></div>  <span id="valueDisplay">&mdash;</span></div><div>  <button type="button" id="stopButton" class="green90x24">Stop</button>  (stops the progress bar pulse checking)</div>

This markup creates four primary elements:

  • A Start button that will start a lengthy operation and use our plugin to monitor its progress.
  • A <div> to be transformed into the progress bar.
  • A <span> to show the completion percentage as text.
  • A Stop button that will stop the progress bar from monitoring the lengthy operation.

In action, our test page will look like figure 11.9.

Figure 11.9. The auto-progressbar is monitoring a long-running operation on the server.

 

Note

Because this example uses server-side Ajax operations, it must be run from the Tomcat instance that we set up for the example in chapter 8 (note the 8080 port in the URL). Alternatively, you can run this example remotely by visiting http://www.bibeault.org/jqia2/chapter11/progressbars/autoprogressbar-test.html.

 

Instrumenting the Start button is the most important operation on this page, and that’s accomplished with the following script:

Within the click handler for the Start button, we do two things: kick off the lengthy operation, and create the auto-progressbar.

We post to the URL /jqia2/lengthyOperation, which identifies a process on the server that takes approximately 12 seconds to complete . We’ll get to the success callback in a moment, but first let’s skip ahead to the creation of the auto-progressbar.

We call our new plugin with values that identify a server-side resource, /jqia2/ checkProgress, which identifies the process that checks the status of our long-running process and returns the completion percentage as its response. How this is done on the server is completely dependent upon how the backend of the web application is written and that’s well beyond the scope of this discussion. (For our example, two separate servlets are used, using the servlet session to keep track of progress.) The change handler for the progress bar causes the onscreen display of the completion value to be updated.

Now let’s backtrack to the success handler for the long-running operation . When the operation completes, we want to do two things: stop the progress bar, and make sure that the progress bar reflects that the operation is 100 percent done. We easily accomplish this by first calling the stop method of our plugin, followed by a call to the value method. The change handler for the progress bar will update the text display accordingly.

We’ve created a really useful plugin using a progress bar. Now let’s discuss some styling tips for progress bars.

11.3.4. Styling progress bars

When an element is transformed into a progress bar, the class name ui-progressbar is added to it, and the <div> element created within this element to depict the value is classed with ui-progressbar-value. We can use these class names for CSS rules that augment the style of these elements as we see fit.

For example, you might want to fill the background of the inner element with an interesting pattern, rather than the theme’s solid color:

.ui-progressbar-value {  background-image: url(interesting-pattern.png);}

Or you could make the progress bar even more dynamic by supplying an animated GIF image as the background image.

Progress bars calm the psyches of our users by letting them know how their operations are progressing. Next, let’s delight our users by limiting how much they need to type to find what they’re looking for.

11.4. Autocompleters

The contemporary acronym TMI, standing for “too much information,” is usually used in conversation to mean that a speaker has revealed details that are a tad too intimate for the listening audience. In the world of web applications, “too much information” refers not to the nature of the information, but the amount.

Although having the vast amount of information that’s available on the web at our fingertips is a great thing, it really is possible to have too much information—it’s easy to get overwhelmed when fed a deluge of data. Another colloquial expression that describes this phenomenon is “drinking from a fire hose.”

When designing user interfaces, particularly those for web applications, which have the ability to access huge amounts of data, it’s important to avoid flooding a user with too much data or too many choices. When presenting large data sets, such as report data, good user interfaces give the user tools to gather data in ways that are useful and helpful. For example, filters can be employed to weed out data that isn’t relevant to the user, and large sets of data can be paged so that they’re presented in digestible chunks. This is exactly the approach taken by our DVD Ambassador example.

As an example, let’s consider a data set that we’ll be using in this section: a list of DVD titles, which is a data set consisting of 937 titles. It’s a large set of data, but still a small slice of larger sets of data (such as the list of all DVDs ever made, for example).

Suppose we wished to present this list to users so that they could pick their favorite flick. We could set up an HTML <select> element that they could use to choose a title, but that would hardly be the friendliest thing to do. Most usability guidelines recommend presenting no more than a dozen or so choices to a user at a time, let alone many hundreds! And usability concerns aside, how practical is it to send such a large data set to the page each time it’s accessed by potentially hundreds, thousands, or even millions of users on the web?

jQuery UI helps us solve this problem with an autocomplete widget—a control that acts a lot like a <select> dropdown, but filters the choices to present only those that match what the user is typing into a control.

11.4.1. Creating autocomplete widgets

The jQuery autocomplete widget augments an existing <input> text element to fetch and present a menu of possible choices that match whatever the user types into the input field. What constitutes a match depends on the options we supply to the widget upon creation. Indeed, the autocomplete widget gives us a great deal of flexibility in how to provide the list of possible choices, and how to filter them given the data supplied by the user.

The syntax for the autocomplete() method is as follows:

 

Command syntax: autocomplete
autocomplete(options)autocomplete('disable')autocomplete('enable')autocomplete('destroy')autocomplete('option',optionName,value)autocomplete('search',value)autocomplete('close')autocomplete('widget')

Transforms the <input> elements in the wrapped set into an autocomplete control.

Parameters

options

(Object) An object hash of the options to be applied to the elements in the wrapped set, as described in table 11.7, making them autocompleters.

'disable'

(String) Disables autocomplete controls.

'enable'

(String) Re-enables disabled autocomplete controls.

'destroy'

(String) Reverts any elements transformed into autocomplete controls to their previous state.

'option'

(String) Allows option values to be set on all elements of the wrapped set, or to be retrieved from the first element of the wrapped set (which should be an autocomplete element), based upon the remaining parameters. If specified, at least the optionName parameter must also be provided.

optionName

(String) The name of the option (see table 11.7) whose value is to be set or returned. If a value parameter is provided, that value becomes the option’s value. If no value parameter is provided, the named option’s value is returned.

value

(Object) The value to be set for the option identified by the optionName parameter (when used with 'option'), or the search term (if used with 'search').

'search'

(String) Triggers a search event using the specified value, if present, or the content of the control. Supply an empty string to see a menu of all possibilities.

'close'

(String) Closes the autocomplete menu, if open.

'widget'

(String) Returns the autocomplete element (the one annotated with the uiautocomplete class name).

Returns

The wrapped set, except for the case where an option, element, search result, or handle value is returned.

 

For such a seemingly complex control, the list of options available for autocomplete controls is rather sparse, as described in table 11.7.

Table 11.7. Options for the jQuery UI autocompleters

Option

Description

In Lab?

change

(Function) Specifies a function to be established on the autocompleters as an event handler for autocompletechange events. See the description of the autocomplete events in table 11.8 for details on the information passed to this handler.

close

(Function) Specifies a function to be established on the autocompleters as an event handler for autocompleteclose events. See the description of the autocomplete events in table 11.8 for details on the information passed to this handler.

delay

(Number) The number of milliseconds to wait before trying to obtain the matching values (as specified by the source option). This can help reduce thrashing when non-local data is being obtained by giving the user time to enter more characters before the search is initiated.

If omitted, the default is 300 (0.3 seconds).

disabled

(Boolean) If specified and true, the widget is initially disabled.

 

focus

(Function) Specifies a function to be established on the autocompleters as an event handler for autocompletefocus events. See the description of the autocomplete events in table 11.8 for details on the information passed to this handler.

minLength

(Number) The number of characters that must be entered before trying to obtain the matching values (as specified by the source option). This can prevent too large a value set from being presented when a few characters isn’t enough to whittle the set down to a reasonable level.

The default value is 1 character.

open

(Function) Specifies a function to be established on the autocompleters as an event handler for autocompleteopen events. See the description of the autocomplete events in table 11.8 or details on the information passed to this handler.

search

(Function) Specifies a function to be established on the autocompleters as an event handler for autocompletesearch events. See the description of the autocomplete events in table 11.8 for details on the information passed to this handler.

select

(Function) Specifies a function to be established on the autocompleters as an event handler for autocompleteselect events. See the description of the autocomplete events in table 11.8 for details on the information passed to this handler.

source

(String|Array|Function) Specifies the manner in which the data that matches the input data is obtained. A value must be provided or the autocomplete widget won’t be created. This value can be a string representing the URL of a server resource that will return matching data, an array of local data from which the value will be matched, or a function that serves as a general callback from providing the matching values.

See section 11.4.2 for more information on this option.

As you might have guessed, an Autocompleters Lab (shown in figure 11.10) has been provided. Load it from chapter11/autocompleters/lab.autocompleters.html and follow along as you review the options.

Figure 11.10. The jQuery UI Autocompleters Lab shows us how a large result set can be narrowed down as more data is entered.

 

Note

In this Lab, the URL variant of the source option requires the use of server-side Ajax operations. It must be run from the Tomcat instance we set up for the example in chapter 8 (note the 8080 port in the URL). Alternatively, you can run this example remotely by visiting http://www.bibeault.org/jqia2/chapter11/autocompleters/lab.autocompleters.html.

 

Except for source, these options are all fairly self-explanatory. Leaving the source option at its default setting, use the Autocompleters Lab to observe the events that transpire and the behavior of the minLength and delay options until you feel that you have grasped them.

Now let’s see what it takes to provide source data for this widget.

11.4.2. Autocomplete sources

The autocomplete widget gives us a lot of flexibility for providing the data values that match whatever the user types in.

Source data for the autocompleters takes the form of an array of candidate items, each of which has two properties:

  • A value property that represents the actual values. These are the strings that are matched against as the user types into the control, and they’re the values that will be injected into the control when a menu item is selected.
  • A label property that represents the value, usually as a shorter form. These strings are what is displayed in the autocomplete menu, and they don’t participate in the default matching algorithms.

This data can come from a variety of sources.

For cases where the data set is fairly small (dozens, not hundreds or more), the data can be provided as a local array. The following example is taken from the Autocompleters Lab and provides candidate data that uses usernames as labels and full names as the values:

var sourceObjects = [  { label: 'bear', value: 'Bear Bibeault'},  { label: 'yehuda', value: 'Yehuda Katz'},  { label: 'genius', value: 'Albert Einstein'},  { label: 'honcho', value: 'Pointy-haired Boss'},  { label: 'comedian', value: 'Charlie Chaplin'}];

When displayed, the labels (usernames) are what appear in the autocomplete menu, but matching is performed on the values (full names), and the value is what is set into the control upon selection.

This is handy when we want to represent longer data with shorter values in the menus, but for many cases, perhaps even most, the label and the value will be the same. For these common cases, jQuery UI lets us specify the data as an array of strings, and takes the string value to be both the label and the value.

 

Tip

When providing objects, if only one of label or value is specified, the provided data is automatically used for both label and value.

 

The entries don’t have to be in any particular order (such as sorted) for the widget to work correctly, and matching entries will be displayed in the menu in the order that they appear within the array.

When local data is used, the matching algorithm is such that any candidate value that contains what the user has typed, called the term, is deemed to match. If this isn’t what you want—let’s say you only want to match values that begin with the term—fear not! There are two more-general ways to supply the source data that give us complete control over the matching algorithm.

For the first of these schemes, the source can be specified as the URL of a server-side resource that returns a response containing the data values that match the term, which is passed to the resource as a request parameter named term. The returned data should be a JSON response that evaluates to one of the formats supported for local data, usually an array of strings.

Note that this variant of source is expected to perform the search and return only the matching elements—no further processing of the data will take place. Whatever values are returned are displayed in the autocomplete menu.

When we need maximum flexibility, another scheme can be used: a callback function can be supplied as the source option, and it’s called whenever data is needed by the widget. This callback is invoked with two parameters:

  • An object with a single property, term, that contains the term to be matched.
  • A callback function to be called, which is passed the matching results to be displayed. This set of results can be either of the formats accepted as local data, usually an array of strings.

This callback mechanism offers the most flexibility, because we can use whatever mechanisms and algorithms we want to turn the term into a set of matching elements. A skeleton for how to use this variant of source is as follows:

$('#control').autocomplete({  source: function(request,response) {    var term = request.term;    var results;    // algorithm here to fill results array    response(results);  }});

As with the URL variant of source, the result should contain only those values that are to be displayed in the autocomplete menu.

Play around with the source options in the Autocompleters Lab. A few things to note about the different source options in the Lab:

  • The local string option provides a list of 79 values, all of which begin with the letter F.
  • The local object option provides a short list of usernames for labels, and full names as values. Note how the matching occurs on the values, not the labels. (Hint: enter the letter b.)
  • For the URL variant, the backend resource only matches values that begin with the term. It uses a different algorithm than when local values are supplied (in which the term can appear anywhere within the string). This difference is intentional and is intended to emphasize that the backend resource is free to employ whatever matching criteria it likes.
  • The callback variant simply returns the entire value set of 79 F-titles provided by the local option. Make a copy of the Lab page, and modify the callback to play around with whatever algorithm you’d like to filter the returned values.

Various events are triggered while an autocomplete widget is doing its thing. Let’s see what those are.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.166.246