CHAPTER 19

image

Using the Autocomplete and Accordion Widgets

In this chapter, I describe the jQuery UI autocomplete and accordion widgets. They are more complex than the widgets I showed you in Chapter 18, but they follow the same pattern of settings, methods, and events. These are highly configurable, flexible, and clever user interface controls, and used wisely they can significantly enhance the appearance and usability of your documents and web applications. Table 19-1 provides the summary for this chapter.

Table 19-1. Chapter Summary

Problem Solution Listing
Add the jQuery UI autocomplete feature to an input element Use the autocomplete method 1, 2
Obtain autocomplete suggestions from a remote server Set the source setting to a URL 3–5
Generate autocomplete suggestions dynamically Specify a function for the source setting 6
Return autocomplete results from an asynchronous task Call the response function 7
Control the placement of the autocomplete pop-up Use the position property 8
Control the autocomplete feature programmatically Use the search and close methods 9
Receive notification of the selected autocomplete item Use the focus, select, and change events 10
Modify the autocomplete results shown to the user Handle the response event 11
Override the default autocomplete action Override the default action for the select event 12
Create a jQuery UI accordion widget Use the accordion method 13
Set the height of the accordion and its content panels Use the heightStyle setting 14–16
Change the action that the user has to perform to activate a content element Use the event setting 17
Set the active content element in a jQuery UI accordion Use the active and collapsible settings 18, 19
Change the icons used by an accordion Use the icons setting 20
Receive notifications when the active element in an accordion changes Handle the activate or beforeactivate events 21

JQUERY UI CHANGES SINCE THE LAST EDITION

There have been a number of API changes to the accordion widget in the jQuery UI 1.10 release: the configuration options, methods, and events have all changed. I have listed the changes in the relevant sections throughout this chapter.

The autocomplete widget, which I also describe in this chapter, has a useful new feature: you can control how the pop-up that offers completion choices to the user is positioned (see the section “Positioning the Pop-up”).

Using jQuery UI Autocomplete

The autocomplete widget provides suggestions to the user as she enters values into an input element. Used well, this widget can be a helpful time-saver to the user, speeding up data entry and reducing errors. In the sections that follow, I show you how to create, configure, and use the jQuery UI autocomplete widget.

Creating the Autocomplete Element

You use the autocomplete method on an input element to create an autocomplete widget. Listing 19-1 demonstrates how to set up basic autocompletion.

Listing 19-1.  Creating an Autocompleting Input Element

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <script type="text/javascript">
        $(document).ready(function() {
              
            var flowers = ["Aster", "Daffodil", "Rose", "Peony", "Primula", "Snowdrop",
                           "Poppy", "Primrose", "Petuna", "Pansy"];
              
            $("#acInput").autocomplete({
                source: flowers
            })
        });
    </script>
</head>
<body>
    <form>
        <div class="ui-widget">
            <label for="acInput">Flower Name: </label><input id="acInput"/>
        </div>
    </form>
</body>
</html>

The autocomplete method is applied to HTML elements just like the other jQuery UI methods you have seen and is configured with a map object that defines a source property. This property specifies where the autocomplete entries will come from.

You can use a range of different data sources for the autocomplete values, which I demonstrate later in this chapter. In Listing 19-1, I have used a simple array of values. Figure 19-1 illustrates the way that the autocomplete feature is presented to the user.

9781430263883_Fig19-01.jpg

Figure 19-1. A basic jQuery UI autocomplete element

image Caution  The autocomplete feature doesn’t enforce any kind of validation, and the user can enter any value into the input element, not just those that are defined by the source setting.

There are two screenshots in Figure 19-1. The first shows what happens when I type the letter P. As you can see, the first screenshot shows a list of the data items that contain the letter P. This list includes the flower names that start with P, but it also includes Snowdrop as well (because it contains the letter p). In the second screenshot, I have typed Pe, and jQuery UI shows only the items that contain that combination of letters. The user can continue to type his entry or select one from the autocomplete list.

image Tip  In the document, I put the input element and its label inside a div element that belongs to the ui-widget class. This sets the CSS font properties for the label and input elements to match those used by the autocomplete pop-up. I explain more about how you can use the jQuery UI CSS classes in Chapter 35.

Using an Object Array as the Data Source

An alternative approach is to use an array of objects, rather than strings. This allows me to separate the label that is displayed in the pop-up menu from the value inserted into the input element, as demonstrated by Listing 19-2.

Listing 19-2.  Using an Array of Objects for Autocompletion

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <script type="text/javascript">
        $(document).ready(function() {
  
            var flowers = [{label: "Aster (Purple)", value: "Aster"},
                {label: "Daffodil (White)", value: "Daffodil"},
                {label: "Rose (Pink)", value: "Rose"},
                {label: "Peony (Pink)", value: "Peony"}]
              
            $("#acInput").autocomplete({
                source: flowers
            })
          
        });
    </script>
</head>
<body>
    <form>
        <div class="ui-widget">
            <label for="acInput">Flower Name: </label><input id="acInput"/>
        </div>
    </form>
</body>
</html>

When using an array of objects, the autocomplete feature looks for properties called label and value. The label property is used to create the pop-up list, and the value entry is inserted into the input element if the item is selected. In Listing 19-2, I have added some color information to the labels that is not included in the values when they are selected, as shown in Figure 19-2.

9781430263883_Fig19-02.jpg

Figure 19-2. Using an array of objects to separate labels from values

Configuring Autocomplete

The autocomplete feature supports a number of settings that let you control different aspects of its functionality, as described by Table 19-2. In the sections that follow, I show you how to use these settings to configure the widget.

Table 19-2. Autocomplete Settings

Setting Description
appendTo Specifies the element that the pop-up menu should be appended to. The default is the body element.
autoFocus If set to true, the first item in the list will be given the focus, meaning that the user can select this item by pressing the Return key. The default is false.
delay Specifies the delay (in milliseconds) after a keystroke after which the autocomplete data are updated. The default is 300.
disabled Disables the autocomplete feature when set to true. This setting does not affect the underlying input element. The default is false.
minLength Specifies the minimum number of characters that the user has to type before the autocomplete menu is displayed. The default is 1.
position Sets the position of the pop-up menu relative to the input element.
source Specifies the source of items to be added to the autocomplete menu. There is no default for this setting, which must be specified when calling the autocomplete method.

Using a Remote Data Source

The most interesting autocomplete setting is source because you can use it to work with a wide range of different kinds of data to populate the pop-up menu. I used a JavaScript array in Listing 19-2, which is fine for simple static lists of data. For more complex situations, you can get the list of matching items from a server. All you have to do is specify the URL that will generate the data, as shown in Listing 19-3.

Listing 19-3.  Using a Remote Data Source

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <script type="text/javascript">
        $(document).ready(function() {
              
            $("#acInput").autocomplete({
                source: "http://node.jacquisflowershop.com/auto"
            })
        });
    </script>
</head>
<body>
    <form>
        <div class="ui-widget">
            <label for="acInput">Flower Name: </label><input id="acInput"/>
        </div>
    </form>
</body>
</html>

When jQuery UI needs the list of items for the pop-up autocomplete menu, it will make an HTTP GET request to the specified URL. The characters that the user has typed so far are included in the request query string using the key term. So, for example, if the user has typed the letter s, then jQuery UI will request the following URL:

If the user then types the letter n, jQuery UI will request the following:

This technique is useful when there are a lot of data items and you don’t want to send them all to the client. It can also be useful when the list of items changes dynamically and you want to make sure the user benefits from the latest data available.

The server is responsible for taking the term value from the query string and returning a JSON string representing the array of items to display to the user. Listing 19-4 shows how I have updated the formserver.js script for Node.js script to do just that. (See Chapter 1 for details of obtaining and installing Node.js.)

Listing 19-4.  The Node.js Script to Support Remote Autocompletion

var http = require("http");
var querystring = require("querystring");
var url = require("url");
  
var port = 80;
  
http.createServer(function (req, res) {
    console.log("[200 OK] " + req.method + " to " + req.url);
  
    var flowers = ["Aster", "Daffodil", "Rose", "Peony", "Primula", "Snowdrop",
                    "Poppy", "Primrose", "Petuna", "Pansy"];
  
    var matches = [];
    var term = url.parse(req.url, true).query["term"];
  
    if (term) {
        var pattern = new RegExp("^" + term, "i");
        for (var i = 0; i < flowers.length; i++) {
            if (pattern.test(flowers[i])) {
                matches.push(flowers[i]);
            }
        }
    } else {
        matches = flowers;
    }
  
    res.writeHead(200, "OK", {
        "Content-Type": "application/json",
        "Access-Control-Allow-Origin": "*"
    });
    res.write(JSON.stringify(matches));
    res.end();
  
}).listen(port);
console.log("Ready on port " + port);

This Node.js script uses the same set of flower names as in Listing 19-2 and returns those that match the term sent by the browser. I have changed the search slightly so that only those names that start with the term are returned. For example, if jQuery UI sends a request like the following:

then the Node.js server will return the following JSON:

["Peony","Primula","Poppy","Primrose","Petuna","Pansy"]

Because I am matching at the start of the flower name, Snowdrop is omitted from the list, as you can see in Figure 19-3.

9781430263883_Fig19-03.jpg

Figure 19-3. Obtaining autocomplete entries from a remote server

This is a nice technique but it can generate a lot of requests to the server. This isn’t a problem in my example because I am performing a simple search and my server and browser are on the same network. But for complex searches, across a wide-area network that can suffer delays, the load on the server can become an issue.

The best way to manage the rate at which autocomplete requests are made is to use the minLength and delay settings. The minLength setting specifies the number of characters that the user has to type before jQuery UI makes an autocomplete request to the server. You can use this setting such that you only request data from the server after several characters have been entered, by which time you have enough information to narrow the scope of the search.

The delay setting specifies the amount of time after a key press that the autocomplete information will be requested. You can use this setting to prevent requests from being made when the user is typing quickly. So, if the user types s and n, you can avoid hitting the server for the s list and then immediately doing so again for the sn list. By combining these settings, you can reduce the number of requests and still provide the user with guidance when it is needed. Listing 19-5 shows the uses of these settings.

Listing 19-5.  Using the delay and minLength Settings to Reduce Server Requests

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <script type="text/javascript">
        $(document).ready(function() {
              
            $("#acInput").autocomplete({
                source: "http://node.jacquisflowershop.com/auto",
                minLength: 3,
                delay: 1000
            })
        });
    </script>
</head>
<body>
    <form>
        <div class="ui-widget">
            <label for="acInput">Flower Name: </label><input id="acInput"/>
        </div>
    </form>
</body>
</html>

In Listing 19-5, the initial request to the server won’t be made until the user has entered three characters and has not typed any additional characters for one second.

Using a Function as the Data Source

You can use a function to create a truly customized source for autocomplete entries. You assign the function to the source setting, and it is called each time the autocomplete feature needs to display items to the user. Listing 19-6 demonstrates.

Listing 19-6.  Using a Function to Generate Autocomplete Items

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <script type="text/javascript">
        $(document).ready(function() {
  
            var flowers = ["Aster", "Daffodil", "Rose", "Peony", "Primula", "Snowdrop",
                            "Poppy", "Primrose", "Petuna", "Pansy"];
  
            $("#acInput").autocomplete({
                source: function(request, response) {
                    var term = request.term;
                    var pattern = new RegExp("^" + term, "i");
                      
                    var results = $.map(flowers, function(elem) {
                        if (pattern.test(elem)) {
                            return elem;
                        }
                    })
                    response(results);
                }
            })
        });
    </script>
</head>
<body>
    <form>
        <div class="ui-widget">
            <label for="acInput">Flower Name: </label><input id="acInput"/>
        </div>
    </form>
</body>
</html>

Two arguments are passed to the function. The first argument is an object that has a single property called term. The value of this property is the string of characters that the user has entered into the input element. The second argument is a function that you call when you have generated the list of autocomplete items that you want to show to the user. The argument to this function is an array of strings or objects.

In Listing 19-6, I have reproduced the server-side functionality from Listing 19-5, and I generate an array containing those items that start with the specified term.

image Tip  I processed the contents of the array using the jQuery map utility method, which I describe in Chapter 34.

I then pass the results back to jQuery UI by passing the array as an argument to the response function, as follows:

...
response(results);
...

This seems like an odd way to process the results, but it means that you can call the function after an asynchronous task has completed. In Listing 19-7, you can see how I have used a function that makes an Ajax request to get details of the flowers, performs a local search of the content that is returned, and then calls the response function to give the final result to jQuery UI.

Listing 19-7.  Using a Custom Data Source Function That Performs an Asynchronous Task

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <style type="text/css">
        button {margin-bottom: 5px}
    </style>
    <script type="text/javascript">
        $(document).ready(function () {
            $("#acInput").autocomplete({
                source: function (request, response) {
                    $.getJSON("http://node.jacquisflowershop.com/auto",
                        function(flowers) {
                            var term = request.term;
                            var pattern = new RegExp("^" + term, "i");
  
                            var results = $.map(flowers, function (elem) {
                                if (pattern.test(elem)) {
                                    return elem;
                                }
                            })
                            response(results);
                        });
                }
            })
        });
    </script>
</head>
<body>
    <form>
        <div class="ui-widget">
            <label for="acInput">Flower Name: </label><input id="acInput"/>
        </div>
  
    </form>
</body>
</html>

In this example, I use the getJSON method to get the complete set of flower values from the Node.js server. I search for matches locally and call the response function when I have a set of suggestions to present to jQuery UI.

Positioning the Pop-up

By default, the pop-up that allows the user to select a value will appear beneath the input element, but you can change this by setting the position property—although the syntax for doing so is a little awkward. Listing 19-8 shows an example that changes the position.

Listing 19-8.  Changing the Position of the Pop-up

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <style>
        #target { margin-top: 40px; display:inline-block }
    </style>
    <script type="text/javascript">
        $(document).ready(function () {
  
            var flowers = [{ label: "Aster (Purple)", value: "Aster" },
                { label: "Daffodil (White)", value: "Daffodil" },
                { label: "Rose (Pink)", value: "Rose" },
                { label: "Peony (Pink)", value: "Peony" }]
  
            $("#acInput").autocomplete({
                source: flowers,
                position: {
                    my: "left top",
                    at: "right bottom+20",
                    of: "#target",
                    collision: "fit"
                }
            })
  
        });
    </script>
</head>
<body>
    <form>
        <div class="ui-widget">
            <label for="acInput">Flower Name: </label><input id="acInput"/>
        </div>
    </form>
    <span id="target">Target</span>
</body>
</html>

The position property is configured with an object that specifies properties for different aspects of the policy for placing the pop-up and I have described the four properties I used in the listing in Table 19-3.

Table 19-3. Autocomplete Position Properties

Name Description
my Specifies the part of the pop-up that will be used to determine placement (see below for details of the range of values that can be used)
at Specifies the part of the target element that the pop-up will be positioned relative to (see below for details of the range of values that can be used)
of Specifies the target element that the pop-up will be positioned relative to; this is the input element if omitted, but can be specified as an HTMLElement, a selector, or a jQuery object
collision Specifies how the placement of the pop-up should be adjusted if it overflows the window (Table 19-4 shows the values for this property)

Table 19-4. Autocomplete Position Properties

Name Description
flip jQuery UI checks to see if more of the pop-up can be displayed on the opposite side of the element specified by the of property; the side that will display the greatest amount of the pop-up will be selected
fit jQuery UI moves the pop-up away from the edge of the window
flipfit Combines the behavior of both the flip and fit values
none Tells jQuery UI not to adjust the position of the pop-up

image Tip  The autocomplete pop-up is placed using the jQuery UI position utility feature, which has more configuration options than the ones I use in this chapter. See http://api.jqueryui.com/position for further details.

The my and at properties are set using values that specify horizontal and vertical positions, separated by a space. The horizontal values are left, right, and center and the vertical values are top, bottom, and center. You can also specify offsets from a position, either as a percentage or as a number of pixels. In the listing, you can see that I have set the my, at, and of properties as follows:

...
my: "left top",
at: "right bottom+20",
of: "#target",
...

This combination means the following: position the top-left corner of the autocomplete pop-up against the point which is 20 pixels below the bottom-right corner of the element whose id is target. You would not commonly specify an element using the of property because it breaks the visual association between the pop-up menu and the input element, but I wanted to demonstrate that there is a lot of flexibility in how the pop-up is placed and you can see the effect of these configuration properties in Figure 19-4.

9781430263883_Fig19-04.jpg

Figure 19-4. Configuring the placement of the autocomplete pop-up

The collision property specifies what happens if the pop-up won’t fit in the space available. Table 19-4 describes the supported values for this property.

I selected the fit value for the collision property and this means that jQuery UI will move the pop-up to fit it inside the browser window, as shown in Figure 19-5.

9781430263883_Fig19-05.jpg

Figure 19-5. Moving the autocomplete pop-up to accommodate the edge of the browser window

Using the Autocomplete Methods

The jQuery UI autocomplete feature supports a number of methods that you can use to manipulate the autocomplete process. Table 19-5 describes these methods.

Table 19-5. Autocomplete Methods

Method Description
autocomplete("close") Closes the autocomplete menu
autocomplete("destroy") Removes the autocomplete functionality from the input element
autocomplete("disable") Disables autocomplete
autocomplete("enable") Enables autocomplete
autocomplete("option") Sets one or more options
autocomplete("search", value) Explicitly triggers autocomplete using the specified value; if no value argument is provided, then the contents of the input element are used

The two methods unique to the autocomplete widget are search and close, which you can use to explicitly start and end the autocomplete process, as demonstrated in Listing 19-9.

Listing 19-9.  Using the search and close Methods

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <style type="text/css">
        button {margin-bottom: 5px}
    </style>
    <script type="text/javascript">
        $(document).ready(function() {
              
            var flowers = ["Aster", "Daffodil", "Rose", "Peony", "Primula", "Snowdrop",
                            "Poppy", "Primrose", "Petuna", "Pansy"];
  
            $("#acInput").autocomplete({
                source: flowers
            });
              
            $("button").click(function(e) {
                e.preventDefault();
                switch (this.id) {
                    case "close":
                        $("#acInput").autocomplete("close");
                        break;
                    case "input":
                        $("#acInput").autocomplete("search");
                        break;
                    default:
                        $("#acInput").autocomplete("search", this.id);
                        break;
                }
            });
        });
    </script>
</head>
<body>
    <form>
        <button id="s">S</button>
        <button id="p">P</button>
        <button id="input">Input Content</button>
        <button id="close">Close</button>
        <div class="ui-widget">
            <label for="acInput">Flower Name: </label><input id="acInput"/>
        </div>
    </form>
</body>
</html>

I added button elements and used the jQuery click method to set up different autocomplete method calls. When the buttons marked S or P are pressed, I call the search method, passing in the selected letter as the search value. This triggers the autocomplete feature using the selected letter, irrespective of the contents of the input element, as shown in Figure 19-6.

9781430263883_Fig19-06.jpg

Figure 19-6. Using the search method with a search term

As you can see in the figure, the pop-up menu displays the entries that contain the letter from the button even though the input element contains the word hello.

The Input Content button triggers the autocomplete feature using whatever characters are contained in the input element, as shown in Figure 19-7.

9781430263883_Fig19-07.jpg

Figure 19-7. Searching using the contents of the input element

The final button, Close, calls the close method that dismisses the pop-up menu.

Using the Autocomplete Events

The autocomplete feature defines a number of events, as described in Table 19-6.

Table 19-6. Autocomplete Events

Event Description
change Triggered when the focus leaves the input element after the value has changed
close Triggered when the pop-up menu is closed
create Triggered when the autocomplete is created
focus Triggered when an item in the pop-up menu gains the focus
open Triggered when the pop-up menu is displayed
response Triggered after a search has been completed but before the results are displayed to the user
search Triggered before the list of autocomplete items is generated or requested
select Triggered when an item is selected from the menu

Getting Details of the Selected Item

jQuery UI provides additional information about an event through a second argument, typically called ui. For the change, focus, and select events, jQuery UI gives the ui object an item property that returns an object describing the selected or focused item from the pop-up menu. Listing 19-10 shows how you can use this feature to get information about the item.

Listing 19-10.  Using the ui Object in Event Handlers

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <script type="text/javascript">
        $(document).ready(function() {
  
            var flowers = ["Aster", "Daffodil", "Rose", "Peony", "Primula", "Snowdrop",
                            "Poppy", "Primrose", "Petuna", "Pansy"];
  
            $("#acInput").autocomplete({
                source: flowers,
                focus: displayItem,
                select: displayItem,
                change: displayItem
            })
              
            function displayItem(event, ui) {
                $("#itemLabel").text(ui.item.label)
            }
        });
    </script>
</head>
<body>
    <form>
        <div class="ui-widget">
            <label for="acInput">Flower Name: </label><input id="acInput"/>
            Item Label: <span id="itemLabel"></span>
        </div>
    </form>
</body>
</html>

I have added a span element that I use to display the label property of the selected object. jQuery UI creates objects with label and value properties even when you use a simple string array for the source setting, so you always need to read one of these properties from the ui.item object. In this example, I use the same function to display the item from the focus, select, and change events. You can see the effect in Figure 19-8.

9781430263883_Fig19-08.jpg

Figure 19-8. Getting the details of the selected item

Modifying Search Results

The response event provides an opportunity to modify the results before they are shown to the user. In Listing 19-11, you can see how I handle the response event to prevent the Peony value from being shown.

Listing 19-11.  Handling the response Event

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <script type="text/javascript">
        $(document).ready(function () {
  
            var flowers = ["Aster", "Daffodil", "Rose", "Peony", "Primula", "Snowdrop",
                            "Poppy", "Primrose", "Petuna", "Pansy"];
  
            $("#acInput").autocomplete({
                source: flowers,
                response: filterResults
            });
  
            function filterResults(event, ui) {
                for (var i = 0; i < ui.content.length; i++) {
                    if (ui.content[i].label == "Peony") {
                        ui.content.splice(i, 1);
                    }
                }
            }
        });
    </script>
</head>
<body>
    <form>
        <div class="ui-widget">
            <label for="acInput">Flower Name: </label><input id="acInput"/>
        </div>
    </form>
</body>
</html>

I have defined a function called filterResults to handle the response event. Within the function, I enumerate the results that will be presented to the user, which are available as an array accessed through the ui.content property. The handler function for the response event must modify the array directly and so I use the splice method to remove the Peony entry from the ui.content array.

Overriding the Default Select Action

The select event has a default action, which is to replace the contents of the input element with the contents of the value property of the item selected from the pop-up menu. This is exactly what is required most of the time, but this event can be used to supplement the default action or prevent it and do something entirely different. Listing 19-12 contains an example of supplementing the default by setting the value of a related field.

Listing 19-12.  Overriding the Default Action of the select Event

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <script type="text/javascript">
        $(document).ready(function() {
  
            var flowers = ["Aster", "Daffodil", "Rose"];
              
            var skus = { Aster: 100, Daffodil: 101, Rose: 102};
              
            $("#acInput").autocomplete({
                source: flowers,
                select: function(event, ui) {
                    $("#sku").val(skus[ui.item.value]);
                }
            })
        });
    </script>
</head>
<body>
    <form>
        <div class="ui-widget">
            <label for="acInput">Flower Name: </label><input id="acInput"/>
            <label for="sku">Stock Keeping Unit: </label><input id="sku"/>
        </div>
    </form>
</body>
</html>

image Tip  I described default actions for events in Chapter 9.

When the select event is triggered, my handler function uses the ui argument to get the value of the selected item and set the value of a related field—in this case, the stock keeping unit, which is obtained from the skus object. In this way, I can help the user by providing default values for other fields based on the initial selection. This can be helpful in lots of situations, especially when selecting items such as shipping addresses. You can see the result in Figure 19-9, although this is an example where you should load a browser to get the full effect. The HTML for this document and all of the other examples in this book are freely available in the Source Code/Download area of the Apress web site (www.apress.com).

9781430263883_Fig19-09.jpg

Figure 19-9. Using the select event to populate another field

Using the jQuery UI Accordion

The accordion widget takes a set of content elements and presents them so that at most one is visible to the user. The visible content is hidden when the user selects another, creating an effect that is reminiscent of the bellows in the musical instrument of the same name.

Accordions are great for presenting content that can be broken into discrete sections when you don’t want to overwhelm the user by displaying it all at once. Ideally, the individual content sections share some overarching theme that can be expressed using simple headers.

Creating the Accordion

The jQuery UI accordion widget is applied using the accordion method, as shown in Listing 19-13.

Listing 19-13.  Creating an Accordion

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script src="handlebars.js"></script>
    <script src="handlebars-jquery.js"></script>
    <script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
    <link rel="stylesheet" type="text/css" href="styles.css"/>
    <style type="text/css">
        #accordion {margin: 5px}
        .dcell img {height: 60px}
    </style>
    <script id="flowerTmpl" type="text/x-jquery-tmpl">
        {{#flowers}}
        <div class="dcell">
            <img src="{{product}}.png"/>
            <label for="{{product}}">{{name}}:</label>
            <input name="{{product}}" value="0" />
        </div>
        {{/flowers}}
    </script>
    <script type="text/javascript">
        $(document).ready(function () {
            var data = {
                flowers: [{ "name": "Aster", "product": "aster" },
                { "name": "Daffodil", "product": "daffodil" },
                { "name": "Rose", "product": "rose" },
                { "name": "Peony", "product": "peony" },
                { "name": "Primula", "product": "primula" },
                { "name": "Snowdrop", "product": "snowdrop" },
                { "name": "Carnation", "product": "carnation" },
                { "name": "Lily", "product": "lily" },
                { "name": "Orchid", "product": "orchid" }]
            };
  
            var elems = $("#flowerTmpl").template(data).filter("*");
            elems.slice(0, 3).appendTo("#row1");
            elems.slice(3, 6).appendTo("#row2");
            elems.slice(6).appendTo("#row3");
  
            $("#accordion").accordion();
  
            $("button").button();
        });
    </script>
</head>
<body>
    <h1>Jacqui's Flower Shop</h1>
    <form method="post" action="http://node.jacquisflowershop.com/order">
        <div id="accordion">
            <h2><a href="#">Row 1</a></h2>
            <div id="row1"></div>
            <h2><a href="#">Row 2</a></h2>
            <div id="row2"></div>
            <h2><a href="#">Row 3</a></h2>
            <div id="row3"></div>
        </div>
        <div id="buttonDiv"><button type="submit">Place Order</button></div>
    </form>
</body>
</html>

The most important part of this example is the content of the div element whose id is accordion.

...
<div id="accordion">
    <h2><a href="#">Row 1</a></h2>
    <div id="row1"></div>
  
    <h2><a href="#">Row 2</a></h2>
    <div id="row2"></div>
  
    <h2><a href="#">Row 3</a></h2>
    <div id="row3"></div>
</div>
...

I have changed the formatting to make the structure more obvious. The top-level div element is the one that is targeted with the accordion method. jQuery UI looks at the contents of the div for header elements (the h1 to h6 elements) and breaks up the content so that each header is associated with the element that follows it. In this case, I have used h2 elements as headers, each of which is followed by a div element. I use data templates to populate these div elements with details of the products offered by the flower shop.

Notice that I have added an a element within each h2 element. This is the means by which the title for each content section is specified. You can see how jQuery UI transforms the top-level div element and its contents in Figure 19-10.

9781430263883_Fig19-10.jpg

Figure 19-10. A jQuery UI accordion

image Tip  Setting the href attribute to # is a common technique when defining a elements that are going to be used solely for JavaScript. I have used this approach because it makes the example simpler, but I generally recommend using jQuery to insert the a elements dynamically so that they don’t interfere with non-JavaScript users.

When the accordion is created, the first content section is displayed while the others are hidden. The content of the a elements are used as the labels for each section, and clicking a label closes the current section and opens the selected one (there is a nice animation effect during the transition that I can’t show using screenshots). You can see the effect of clicking the headers in Figure 19-11.

9781430263883_Fig19-11.jpg

Figure 19-11. The accordion transitions

Configuring the Accordion

The accordion supports a number of configuration settings that can be used to fine-tune its behavior. Table 19-7 describes these settings, and I show you how to use these settings to configure the widget in the sections that follow.

Table 19-7. Accordion Settings

Setting Description
active Gets or sets the content element to be displayed. The default is to initially display the first content element.
animate Specifies the animation that will be used during the transition from one content element to another. See Chapter 35 for details of the jQuery UI animations.
collapsible When true, all of the content sections can be collapsed. The default is false.
disabled When true, the accordion is disabled. The default is false.
event Specifies the event from the header element that triggers the transition to another content element. The default is click.
header Specifies which elements will be used as headers.
heightStyle Controls the height of the accordion and its panels.
icons Specifies the icons used in the accordion.

image Tip  In jQuery UI 1.10, the animate setting replaces the animated setting; the heightStyle option replaces the autoHeight, clearStyle, and fillSpace settings; and a new property name is used to specify the icons used for the selected content panels (activeHeader) when using the icons property.

Setting the Height of the Accordion

The heightStyle property controls the height of the accordion and its panels. There are three supported values, which I have described in Table 19-8.

Table 19-8. The Values for the heightStyle

Name Description
auto All of the panels will be set to the height of the tallest panel
fill Expands the accordion to fill the parent element’s height
content Each panel will be as high as its content

You can set the height of the accordion based on either the height of the content elements or the height of the parent element. The most common technique is to rely on the auto value, which is the default, and which sets all of the content elements to be the same height (the height of the tallest content element) and sizes the accordion based on that height.

This is the approach I used in the previous example, although some caution is required when using content elements that contain images, especially when the img elements are inserted into the document using jQuery. The problem is that the call to the accordion method can be made before all of the images are loaded, which causes jQuery UI to get misleading information from the browser about the height of the content elements. In my example document, the height of the content div elements is 55 pixels before the images are loaded and 79 pixels when they are loaded. You can tell whether you have hit this problem when the accordion shows unexpected scrollbars to display the content, as shown in Figure 19-12.

9781430263883_Fig19-12.jpg

Figure 19-12. Problems caused by incorrect height information

jQuery UI doesn’t detect the change in the content element’s height when the images are loaded and ends up displaying the content incorrectly. To address this, you need to provide information about the height that the content elements will be once you load all of the external resources. There are lots of ways of doing this, and in the example, I chose to set the CSS height property for the img elements in a style element, as follows:

...
<style type="text/css">
    #accordion {margin: 5px}
    .dcell img {height: 60px}
</style>
...

Image issues aside, the auto value is useful when you want consistent heights for each of the content elements, but it can lead to some unappealing visuals when there is a large disparity between the sizes of the content elements. Listing 19-14 shows a script element that inserts the product information elements in a different way.

Listing 19-14.  An Accordion with a Large Height Differential

...
<script type="text/javascript">
    $(document).ready(function () {
  
        var data = {
            flowers: [{ "name": "Aster", "product": "aster" },
            { "name": "Daffodil", "product": "daffodil" },
            { "name": "Rose", "product": "rose" },
            { "name": "Peony", "product": "peony" },
            { "name": "Primula", "product": "primula" },
            { "name": "Snowdrop", "product": "snowdrop" },
            { "name": "Carnation", "product": "carnation" },
            { "name": "Lily", "product": "lily" },
            { "name": "Orchid", "product": "orchid" }]
        };
  
        var elems = $("#flowerTmpl").template(data).filter("*");
        elems.slice(0, 3).appendTo("#row1");
        elems.slice(3, 6).appendTo("#row2");
        elems.slice(6).appendTo("#row3");
  
        $("<h2><a href=#>All</a></h2><div id=row0></div>").prependTo("#accordion")
            .filter("div").append($("#row1, #row2, #row3").clone());
  
        $("#accordion").accordion();
  
        $("button").button();
    });
</script>
...

To create an extra-high content element, I have used jQuery to clone the existing content div elements and insert them into a new content element, creating a panel that displays all of the products. This new panel is three times the height of the others, which causes the accordion to display a lot of empty space when the smaller content elements are displayed, as shown in Figure 19-13.

9781430263883_Fig19-13.jpg

Figure 19-13. The effect of the auto setting with a large height difference

If a large expanse of empty space doesn’t suit your application, then you can simple change the heightStyle property to content, as shown in Listing 19-15.

Listing 19-15.  Changing the heightStyle Setting

...
$("#accordion").accordion({
    heightStyle: "content"
});
...

The accordion will now change its height as part of the transition between content elements, as shown in Figure 19-14.

9781430263883_Fig19-14.jpg

Figure 19-14. The accordion resizing itself to accomodate different content heights

This is a neater approach to displaying the content, but it does mean that the layout of the page changes as the accordion resizes itself. This can be annoying to users if key controls are constantly moving around the screen.

Using the Parent to Determine the Height of the Accordion

An entirely different approach is to set the size of the accordion so that it simply fills its parent element. I find this most useful when I am working with content that is generated dynamically such that I don’t have a good handle on the size and I don’t want the layout to be adjusted. You can size the accordion this way through the fill setting, as shown in Listing 19-16.

Listing 19-16.  Sizing the Accordion to Fill the Parent Element

...
<script type="text/javascript">
    $(document).ready(function () {
  
        var data = {
            flowers: [{ "name": "Aster", "product": "aster" },
            { "name": "Daffodil", "product": "daffodil" },
            { "name": "Rose", "product": "rose" },
            { "name": "Peony", "product": "peony" },
            { "name": "Primula", "product": "primula" },
            { "name": "Snowdrop", "product": "snowdrop" },
            { "name": "Carnation", "product": "carnation" },
            { "name": "Lily", "product": "lily" },
            { "name": "Orchid", "product": "orchid" }]
        };
  
        var elems = $("#flowerTmpl").template(data).filter("*");
        elems.slice(0, 3).appendTo("#row1");
        elems.slice(3, 6).appendTo("#row2");
        elems.slice(6).appendTo("#row3");
  
        $("<h2><a href=#>All</a></h2><div id=row0></div>").prependTo("#accordion")
            .filter("div").append($("#row1, #row2, #row3").clone());
  
        $("#accordion").wrap("<div style='height:300px'></div>");
          
        $("#accordion").accordion({
            heightStyle: "fill"
        });
  
        $("button").button();
    });
</script>
...

In this example, I have wrapped the accordion element in a new parent div element that has a fixed size of 300 pixels. When I call the accordion method, I set heightStyle to fill. If the parent is smaller than a content element, then the accordion adds a scrollbar. If the parent is larger than a content element, then padding is added. You can see the application of the scrollbar in Figure 19-15. This arises because the content element that displays all of the flowers is taller than the 300 pixels of the parent element.

9781430263883_Fig19-15.jpg

Figure 19-15. Using the accordion to fill the height of the parent

Changing the Event Type

By default, the user opens and closes content elements by clicking them. You can change this behavior through the event setting, as shown in Listing 19-17.

Listing 19-17.  Using the event Setting

...
<script type="text/javascript">
    $(document).ready(function () {
  
        var data = {
            flowers: [{ "name": "Aster", "product": "aster" },
            { "name": "Daffodil", "product": "daffodil" },
            { "name": "Rose", "product": "rose" },
            { "name": "Peony", "product": "peony" },
            { "name": "Primula", "product": "primula" },
            { "name": "Snowdrop", "product": "snowdrop" },
            { "name": "Carnation", "product": "carnation" },
            { "name": "Lily", "product": "lily" },
            { "name": "Orchid", "product": "orchid" }]
        };
  
        var elems = $("#flowerTmpl").template(data).filter("*");
        elems.slice(0, 3).appendTo("#row1");
        elems.slice(3, 6).appendTo("#row2");
        elems.slice(6).appendTo("#row3");
  
        $("#accordion").accordion({
            event: "mouseover"
        });
  
        $("button").button();
    });
</script>
...

In Listing 19-17, I have used the event setting to specify that content elements should be opened in response to the mouseover event (which I described in Chapter 9). The effect of this change is that as soon as the mouse pointer enters the label for a content element, jQuery UI opens the element and displays its content. I can’t show this effect in a screenshot, but I recommend you load this example to see how it works. It is a neat feature, but I recommend that you use it carefully. Users are generally quick to grasp the idea of clicking the icon to open a section of content, but responding to mouse events can make for a twitchy and surprising user experience.

Selecting the Active Header

The default behavior of the accordion is initially to show the first content element to the user. You can change this behavior using the active property. You set active to the index of the content element you want to display as shown in Listing 19-18.

Listing 19-18.  Using the Active Property

...
<script type="text/javascript">
    $(document).ready(function () {
  
        var data = {
            flowers: [{ "name": "Aster", "product": "aster" },
            { "name": "Daffodil", "product": "daffodil" },
            { "name": "Rose", "product": "rose" },
            { "name": "Peony", "product": "peony" },
            { "name": "Primula", "product": "primula" },
            { "name": "Snowdrop", "product": "snowdrop" },
            { "name": "Carnation", "product": "carnation" },
            { "name": "Lily", "product": "lily" },
            { "name": "Orchid", "product": "orchid" }]
        };
  
        var elems = $("#flowerTmpl").template(data).filter("*");
        elems.slice(0, 3).appendTo("#row1");
        elems.slice(3, 6).appendTo("#row2");
        elems.slice(6).appendTo("#row3");
  
        $("#accordion").accordion({
            active: 1
        });
  
        $("button").button();
    });
</script>
...

The effect is that the accordion opens the row at index 1 (the indexes are zero based, so this is the second content element) initially, as shown in Figure 19-16.

9781430263883_Fig19-16.jpg

Figure 19-16. Selecting the initial content element to display

You can also have no content initially active by setting active to false. If you do this, you must also set the collapsible setting to true. This disables the default policy that one content element must always be visible. Listing 19-19 shows the application of these settings.

Listing 19-19.  Disabling the Initially Active Content Element

...
$("#accordion").accordion({
    active: false,
    collapsible: true
});
...

You can see the effect of these settings in Figure 19-17.

9781430263883_Fig19-17.jpg

Figure 19-17. The accordion with no initially active content element

The accordion works as before, with the exception that there is no initially active content element and that all of the content elements can be closed. This is a useful technique when screen estate is limited and the content in the accordion is not of primary interest to the user.

Changing the Accordion Icons

You can use the icons setting to change the icons used in the accordion content headers. Listing 19-20 provides an example.

Listing 19-20.  Changing the Icons Used by the Accordion

...
$("#accordion").accordion({
    collapsible: true,
    icons: {
        header: "ui-icon-zoomin",
        activeHeader: "ui-icon-zoomout"
    }
});
...

image Tip  In jQuery UI 1.10, the activeHeader property replaced the headerSelected property.

You set icons to be an object that has header and activeHeader properties. The first property specifies the icon to use when the content element is closed, and the second specifies the icon to use when it is open. I tend to use this feature in conjunction with the collapsible setting because it gives a more natural feel when using icons that suggest that user can perform an action. You can see how these icons appear in Figure 19-18.

9781430263883_Fig19-18.jpg

Figure 19-18. Using custom icons for the accordion section headers

Using the Accordion Methods

The jQuery UI accordion defines a number of methods, as described in Table 19-9.

Table 19-9. Accordion Methods

Method Description
accordion("destroy") Removes the accordion functionality from the input element
accordion("disable") Disables the accordion
accordion("enable") Enables the accordion
accordion("option") Sets one or more options
accordion("refresh") Refreshes the size of the widget panels.

image Tip  The activate method was removed in jQuery UI 1.10. Use the active option I described in the previous section instead. The resize method has been replaced with the refresh method.

The refresh method updates the sizes of the accordion panels to reflect changes in the content elements. The effect of size changes depends on the value used for the heightStyle option I described in the previous section. The other methods are those which jQuery UI provides for all elements.

image Tip  As of jQuery UI 1.10, calling the refresh method will also update the set of panels to reflect changes in the content elements, allowing elements to be added or removed.

Using the Accordion Events

The jQuery UI accordion widget supports the three events shown in Table 19-10.

Table 19-10. Accordion Events

Event Description
activate Triggered when a content panel is activated
beforeActivate Triggered just before a content panel is activated
create Triggered when the accordion is created

image Tip  The events defined by the accordion widget have changed in jQuery UI 1.10. The changestart event has been replaced by beforeActivate and the change event has been replaced with activate. The additional ui object passed to handler functions for these events use different property names from the old events, as described in Table 19-11.

Table 19-11. The Properties of the ui Object for the change and changestart Events

Name Description
newHeader The header element for the newly active content element
oldHeader The header element for the previously active content element
newPanel The newly active content element
oldPanel The previously active content element

You can use the beforeActivate and active events to monitor the transition between content elements, as shown in Listing 19-21.

Listing 19-21.  Using the change Event

...
<script type="text/javascript">
    $(document).ready(function () {
  
        var data = {
            flowers: [{ "name": "Aster", "product": "aster" },
            { "name": "Daffodil", "product": "daffodil" },
            { "name": "Rose", "product": "rose" },
            { "name": "Peony", "product": "peony" },
            { "name": "Primula", "product": "primula" },
            { "name": "Snowdrop", "product": "snowdrop" },
            { "name": "Carnation", "product": "carnation" },
            { "name": "Lily", "product": "lily" },
            { "name": "Orchid", "product": "orchid" }]
        };
  
        var elems = $("#flowerTmpl").template(data).filter("*");
        elems.slice(0, 3).appendTo("#row1");
        elems.slice(3, 6).appendTo("#row2");
        elems.slice(6).appendTo("#row3");
  
        $("#accordion").accordion({
            active: false,
            collapsible: true,
            activate: handleAccordionChange
        })
  
        function handleAccordionChange(event, ui) {
            if (ui.oldHeader.length) {
                console.log("Old header: " + ui.oldHeader[0].innerText);
            }
            if (ui.newHeader.length) {
                console.log("New header: " + ui.newHeader[0].innerText);
            }
        }
  
        $("button").button();
    });
</script>
...

I use the activate event to respond to the content element being changed. jQuery UI passes information to the event handler function about the active elements via an additional argument to the handler function, just as for the autocomplete widget. This additional argument, usually given the name ui, defines the properties shown in Table 19-11.

These properties are arrays, which is why I test for the length property before getting the HTMLElement object at the first index and writing the value of the innerText property to the console.

Summary

In this chapter, I showed you the jQuery UI autocomplete and accordion widgets. These follow the same basic pattern I used in Chapter 18 but offer richer functionality and a wider range of configuration options to customize the widgets so that they fit neatly into your web application model. In Chapter 20, I show you the tabs widget.

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

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