Home » Tutorials Advanced Tutorial

Creating an Autosuggest Textbox with JavaScript, Part 2 - Page 3

2.1/5.0 (24 votes total)
Rate:

Nicholas C. Zakas
February 11, 2006


Nicholas C. Zakas
Nicholas C. Zakas is a professional Web designer who specializes in user interface design for Web applications using JavaScript, Dynamic HTML, CSS, XML, and XSLT.


http://www.nczonline.net
Nicholas C. Zakas has written 12 tutorials for JavaScriptSearch.
View all tutorials by Nicholas C. Zakas...

Remember the autosuggest() method from the last article? To implement the dropdown list of suggestions it's necessary to update this method.

The first update is the addition of a second argument which indicates whether or not the type ahead functionality should be used (the reason why will be explained shortly). Naturally, the typeAhead() method should only be called if this argument is true. If there's at least one suggestion, type ahead should be used and the dropdown list of suggestion should be displayed by calling showSuggestions() and passing in the array of suggestions; if there's no suggestions, the dropdown list should be hidden by calling hideSuggestions():

AutoSuggestControl.prototype.autosuggest = function (aSuggestions,
bTypeAhead
) {

    if (aSuggestions.length > 0) {
        if (bTypeAhead) {
            this.typeAhead(aSuggestions[0]);
        }
        this.showSuggestions(aSuggestions);
    } else {
        this.hideSuggestions();

    }
};

You will also remember that this method is called from the suggestion provider's requestionSuggestions() method, which means it too must be updated. This is a fairly easy update; you need only add a second argument and then pass it back into the autosuggest() method when it's called:

StateSuggestions.prototype.requestSuggestions = function (oAutoSuggestControl,
bTypeAhead
) {
    var aSuggestions = [];
    var sTextboxValue = oAutoSuggestControl.textbox.value;

    if (sTextboxValue.length > 0){

        for (var i=0; i < this.states.length; i++) {
            if (this.states[i].indexOf(sTextboxValue) == 0) {
                aSuggestions.push(this.states[i]);
            }
        }
    }

    oAutoSuggestControl.autosuggest(aSuggestions, bTypeAhead);
};

With both of these methods updated, it's now necessary to update the handleKeyUp() method. First, just add the second argument (true) when calling requestSuggestions():

AutoSuggestControl.prototype.handleKeyUp = function (oEvent) {

    var iKeyCode = oEvent.keyCode;

    if (iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode <= 46) || (iKeyCode >= 112 && iKeyCode <= 123)) {
        //ignore
    } else {
        this.provider.requestSuggestions(this, true);
    }
};

This functionality now works exactly as it did previously, but there are a couple of other keys that require special attention: Backspace and Delete. When either of these keys are pressed, you don't want to activate the type ahead functionality because it will disrupt the process of removing characters from the textbox, but there's no reason not to show the dropdown list of suggestions. For the Backspace (key code of 8) and Delete (key code of 46) keys, you can also call requestSuggestions(), but this time, pass in false to indicate that type ahead should not occur:

AutoSuggestControl.prototype.handleKeyUp = function (oEvent) {

    var iKeyCode = oEvent.keyCode;

    if (iKeyCode == 8 || iKeyCode == 46) {
        this.provider.requestSuggestions(this, false);

    } else
if (iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode <= 46) || (iKeyCode >= 112 && iKeyCode <= 123)) {
        //ignore
    } else {
        this.provider.requestSuggestions(this, true);
    }
};

Now when the user is removing characters, suggestions will still be provided and the user can click on one of them to select the value for the textbox. This is acceptable, but to really be usable the autosuggest control needs to respond to keyboard controls.

Adding Key Support

The desired keyboard functionality revolves around three keys: the up arrow, the down arrow and Enter (or Return). When the dropdown suggestion list is displayed, you should be able to press the down arrow to highlight to the first suggestion, then press it again to move to the second, and so on. The up arrow should then be used to move back up the list of suggestions. As each suggestion is highlighted, the value must be placed in the textbox. When the Enter key is pressed, the suggestions should be hidden, leaving the last suggestion to be highlighted in the textbox.

When the user scrolls through the suggestions in the list, you must know which suggestion is current. To do this, a property must be added to the AutoSuggestControl definition as follows:

function AutoSuggestControl(oTextbox, oProvider) {
    this.cur = -1;
    
this.layer = null;

    this.provider = oProvider;
    this.textbox = oTextbox;
    this.init();
}

The cur property stores the index of the current suggestion in the suggestions array. By default, this is set to -1 because this there are no suggestions initially.

When the down arrow key is pressed, the next suggestion in the dropdown list should be highlighted. To encapsulate this functionality, a method named nextSuggestion() will be added. Here's the code:

AutoSuggestControl.prototype.nextSuggestion = function () {
    var cSuggestionNodes = this.layer.childNodes;

    if (cSuggestionNodes.length > 0 && this.cur < cSuggestionNodes.length-1) {
        var oNode = cSuggestionNodes[++this.cur];
        this.highlightSuggestion(oNode);
        this.textbox.value = oNode.firstChild.nodeValue;
    }
};

This method obtains the collection of child nodes in the dropdown layer. Since only the <div/> elements containing the suggestions are child nodes of the layer, the number of child nodes accurately matches the number of suggestions. This number can be used to determine if there are any suggestions (in which case it will be greater than 0) and also if there is a next suggestion (which means that it's greater than cur). To ensure that cur never points to an empty node, it must never be allowed to be larger than the number of child nodes minus 1 (because the last element in a collection with n elements is n-1).

If it these two tests are passed, then cur is incremented and the child node in that position is retrieved and stored in oNode. Next, the node is passed in to highlightSuggestion(), which highlights it and unhighlights the previously highlighted suggestion. From there, the value of the textbox is once again set to the text contained inside of the <div/>.

As you may have suspected, another method to highlight the previous suggestion is also necessary. Here it is:

AutoSuggestControl.prototype.previousSuggestion = function () {
    var cSuggestionNodes = this.layer.childNodes;

    if (cSuggestionNodes.length > 0 && this.cur > 0) {
        var oNode = cSuggestionNodes[--this.cur];
        this.highlightSuggestion(oNode);
        this.textbox.value = oNode.firstChild.nodeValue;
    }
};

The previousSuggestion() method is similar to nextSuggestion(). The main differences are that you need to ensure cur is greater than 0 to proceed (you still must make sure that there are suggestions by checking the number of child nodes in the dropdown layer) and that cur must be decremented instead of incremented. Other than these two changes, the algorithm is the same. Now back to the three keys.

Next page


Add commentAdd comment (Comments: 0)  

Advertisement

Partners

Related Resources

Other Resources

arrow