Please turn on JavaScript. To find out how to do this visit the WebWise JavaScript guide.

Overview

The glow.widgets.AutoSuggest is a widget that adds extra functionality to a form input element. As the user types a word it will display a list of possible suggestions for the user to choose from.

Constructing an AutoSuggest widget

You can create an AutoSuggest widget by calling the constructor with a reference to an HTML input element and a data source to be used to create the suggestions. The HTML input element must be of type text and must be accessible via the currently loaded DOM.

new glow.widgets.AutoSuggest(
    "#inputElementId",	// HTML input element to bind the AutoSuggest to
    myData		// refers to a data source
);

Useful options

You can provide options to customise how the widget looks and functions. In particular you are likely to want to specify what should happen when a suggested item is selected by the user, using the onItemSelect option.

myOpts = {
    onItemSelect: function(e) {
        this.val(e.selectedItem.name); // Updates the binded HTML input element with the selected value

    }
}

new glow.widgets.AutoSuggest(
    "#inputElementId",
    myData,
    myOpts
);

Providing data to AutoSuggest

You must pass data to the AutoSuggest constructor to be used to search against when the user types a word into the inputElement. The most straightforward way to do that is to provide a JSON data object: a simple array of strings or an array of objects.

myData = [
    { name: "AliceBlue",    hex: "F0F8FF" },
    { name: "AntiqueWhite", hex: "FAEBD7" },
    { name: "Aqua",         hex: "00FFFF" }
    // etc...
];

If you use an array of strings, then those strings will be what are used as the 'index', the strings used to determine if a match is made when compared to the user input. If you use an array of objects then, by default, the name property is used as the index (or you can specify or create your own index, as shown below).

Using data from a URL

Instead of a JavaScript array you can pass a single string to the AutoSuggest constructor: a URL that refers to a either a static JS file or a server-side process that will return a JSON data object. In order to minimise how many hits your server receives from this widget you control exactly when and how often the data is loaded from that URL, using the loadData() method. For example this would load the data only once, immediately after the widget is constructed:

new glow.widgets.AutoSuggest(
    myInputElement,
    "colornames.js", // URL to data
    myOpts
).loadData(); // load it now

Try it: (type the name of a web-safe colour)

Note that the data is retrieved using XHR so your browser will enforce the "same origin" security policy - the data URL must be on the same server as the webpage displaying it.

Fine control over when data is loaded

If you know you are loading a large or computationally expensive data object, you may not want to load it regardless of whether it is needed or not. A lazier approach would be to only load it only when the user has actually entered a few characters and shown that they definitely need the data. You could accomplish this by putting your loadData() call in a opts.onInputChange handler which checks to see if there is enough input to warrant getting the data.

new glow.widgets.AutoSuggest(
	myInputElement,
	"colornames.js", // URL to data
	{
		onItemSelect: function(e) {
			this.val(e.selectedItem.name);
		},
		onInputChange : function(event) {
			// only hit the server when we have 2 or more chars
			if (this.val().length < 2) {
				event.preventDefault(); // prevent any lookups
				this.hide();            // and tidy up behind ourselves?
			}
			else {
				this.loadData();
				this.find();
			}
		}
	}
)

Try it: (type the name of a web-safe colour)

Note that there is a built-in optimisation that means if the exact same URL is loaded several times in a row, the widget will only actually make the request the first time. So in the above example the server request only happens once, even though the user may type many more than 2 characters.

Updating data from URL dynamically

Often, when using a URL as a datasource, the data will only need to be downloaded from the server once. But you might want to download additional or different data after the user has entered some text. You can create a dynamic dataSource by including the special string {input} somewhere in your URL. This string will be replaced by whatever the current url-escaped value of the inputElement is.

To make a dynamic URL refresh itself, you can call the loadData method from inside an onInputChange handler.

Example

new glow.widgets.AutoSuggest(
    myInputElement,
    "colornames.js?color={input}", // a dynamic URL
    {
        onInputChange : function(event) {
            this.loadData(); // refresh the data from the URL
        }
    }
);

Try it: (type the name of a web-safe colour)

Use this technique with caution, it could cause many unnecessary hits to the server as the user is typing.

Dealing with non-JavaScript responses from the server

It may be that the server returns its data as unformatted text. In that case you can provide a parseData option to convert the text of the server response into a usable JavaScript array.

Note: When the you are using an object as the dataSource and you do not specify an index property, the first property needs to be called 'name'. For example:

JSON

[
	{
		name: "Apple Flan"
	},
	{
		name: "Easy Shortbread"
	},
	{
		name: "Apple FlapJack"
	},
	{
		name: "Flambe of Brandied Apple Ice"
	}
];

Here is an example that returns the JSON above, note the structure of the server response and how the parseData function handles it.

Server response

Apple Flan
Easy Shortbread
Apple FlapJack
Flambe of Brandied Apple Ice

JavaScript

myOpts = {

	parseData: function(response) {

		// Create dataSource object to return
		var dataObject = [];

		// Get the response from the server, splitting the 
		// response of a multi-lined file into an array
		var lines = response.text().split("\n");

		// For each entry in the array...
		glow.lang.map(lines, function(line) {

			// Add a JSON object with the property
			// 'name' onto the dataSource array
			dataObject.push({name:line});

		});

		return dataObject;

	}

}

Using a function to generate data

You may want to generate data dynamically on the client-side, based on what the user has entered into the input element. Simply pass a function to the Autosuggest constructor as the dataSource.

Example

new glow.widgets.AutoSuggest(
    myInputElement,
    function() {
        // generate JSON here, possibly based on this.val()
        if (this.val() == "a") {
            return [
                {name: "allspice"}, {name: "anise"}, {name: "apple-mint"}
            ];
        }
        else return [];
    },
    myOpts
);

Try it: (type the name of a spice)

Displaying the results

When one or more matches are found, each suggestion will be displayed in a list below the input element. You can specify how the result will be displayed using the formatItem option.

Example

myOpts = {
    formatItem: function(item) {
        var html = '<div ';
        html += 'class="swatch" style="background-color:#'+item.hex+'">';
        html += '</div>';
        html += item.name;
        return html;
    },
    activeOnShow: false
}

Try it: (type the name of a web-safe colour)

Determining when an item matches

By default, the text entered into the input element is considered a "match" with any data item if the indexed text begins with the input text. For example the color name "LightYellow" would be considered a match (case insensitive) with the input string "light". You can override this behaviour by providing an opts.isMatch function.

Example

myOpts = {
    isMatch: function(indexedWord, inputWord) {
        return (
            // only try to match when we have more than 1 character
            inputWord.length > 1
            // match anywhere in the word
            && indexedWord.indexOf(inputWord) > -1
        );
    },
    formatItem: function(item) { // underline the matched bit
        var pat = new RegExp(glow.lang.trim(this.getValue()), 'i');
        return item.name.replace(pat, "<u>$&<"+"/u>");
    }
}

Try it: (type the name of a web-safe colour)

Changing the appearance of the widget

You can specify how you want your widget to appear by including a theme and an anim option. By default the theme will be "light", and no animation.

Example

myOpts = {
    theme: "dark", // or "light"
    anim: "roll"   // or "fade"
}

Try it: (type the name of a web-safe colour)

Adding word-completion

The opts.complete option activates a feature of AutoSuggest that will display the completed value of the currently active suggestion.

Example

myOpts = {
    complete: true
}

Try it: (type the name of a web-safe colour)

Indexing your data

If the data object you provide is an array of objects, each object with one or more properties (keys and values), you must specify which of those properties will be used to compare with the user input to see if there is a match. The property name you specify will be the "index property." By default, if you don't specify any index property, AutoSuggest will attempt to use a property named "name." Or you can specify a opts.index that will be either a single property name, an array of property names, or a function that will generate index values.

Example

Assuming your data object is equivalent to the following...

[
    {
      name: "Apple Flan",
      ingredients: ["apples", "brown sugar", "pastry"],
      chef: "Delia Smith"
    },
    {
      name: "Easy Shortbread",
      ingredients: ["butter", "caster sugar", "flour"],
      chef: "Delia Smith"
    },
    {
      name: "Apple FlapJack",
      ingredients: ["oats", "apples", "golden syrup"],
      chef: "Jamie Oliver"
    },
    {
      name: "Flambe of Brandied Apple Ice",
      ingredients: ["apples", "brandy", "golden syrup"],
      chef: "Heston Blumenthal"
    }
 ];

If no index is specified in your options object, then the default name property will be used as the index, if it exists.

myOpts = {
    onItemSelect: function(e) {
        this.val(e.selectedItem.name);
    },
    formatItem: function(item) {
        var html = item.chef + "'s <br />";
        html += "<b>" + item.name + "</b><br />";
        html += "<small>made with: "+item.ingredients.join(", ")+"</small>";
        return html;
    },
    width: 400
}

Try it: (type the name of a recipe)

Or you can specify any property name, and AutoSuggest will use that as the index instead.

Example

myOpts = {
    index: "ingredients"
}

Try it: (type the name of an ingredient)

Or you can also specify a series of property names, and AutoSuggest will use all of them as the index.

Example

myOpts = {
    index: ["chef", "name"]
}

Try it: (type the name of a chef or a recipe)

Finally, you can specify a function that will return an array of values to be indexed.

Example

myOpts = {
    // map each word of the recipe name to the data item
    index: function(item) { return item.name.split(" "); }
}

Try it: (type any word in the name of a recipe)

What happens when a result is selected

When a user selects one of the displayed results, you will likely want some response to occur. By default nothing will happen, but you can specify what you want to happen by creating an opts.onItemSelect handler.

myOpts = {
    onItemSelect: function(event) {
        alert(event.selectedItem.name);
    }
}

Try it: (type any word in the name of a recipe)

BBC © 2014 The BBC is not responsible for the content of external sites. Read more.

This page is best viewed in an up-to-date web browser with style sheets (CSS) enabled. While you will be able to view the content of this page in your current browser, you will not be able to get the full visual experience. Please consider upgrading your browser software or enabling style sheets (CSS) if you are able to do so.