DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 11.23 Creating Auto-Complete Text Fields

11.23.1 Problem

You want to create auto-complete input text fields in which entering a few letters automatically fills in a previously entered value.

11.23.2 Solution

Use a shared object to store the previously entered values for a text field. Then use the onChanged( ), onSetFocus( ), and onKillFocus( ) methods of the text field to create, position, size, and populate a list box with possible values.

11.23.3 Discussion

Internet Explorer and similar browsers offer a feature that remembers values that users have previously entered for a text field in an HTML form. When a user brings focus to the text field, she can use the arrow keys or the mouse to select from a list of previously entered values. This feature can help the user save time typing if she wants to fill out the form with values similar to those entered previously. Flash text fields do not have this kind of functionality built into them, but with a little bit of work, you can add this feature to your forms.

You can use a local shared object to store previously entered values for text fields within a domain. Then you can use a list box to display those values to the user when she brings focus to a text field. As the user types into the text field, the values in the list box should be updated to partially match the string she has entered into the text field. For example, if the user types a "t" in the text field, the list box should display only those values that begin with "t." And if the user selects a value from the list box (either by mouse or by keyboard), that value should automatically be placed within the text field.

Follow these steps to create auto-completing text fields in your movie:

  1. Create a new Flash document or open an existing one.

  2. On the main timeline add a new layer named AutoCompleteCode.

  3. Add a list box symbol to the movie's Library by dragging the ListBox component from the Components panel onto the Stage. Then delete the instance from the Stage (we will add the list box to the movie using ActionScript). The symbol will remain in the movie's Library. The ListBox symbol is automatically linked for export.

  4. Open the Actions panel and add the following code to the first frame of the AutoCompleteCode layer.

  5. Make sure you have assigned names to the input text fields in your movie and then test.

Here is the auto-complete code. Add it to an AutoCompleteText.as file. (And remember that you can find the completed AutoCompleteText.as file at http://www.person13.com/ascb.)

// Create the local shared object at the top level of the domain so that all text
// fields in all Flash movies in the domain can access it.
TextField.so = SharedObject.getLocal("textfieldAutoComplete", "/");

// When the user types into the text field or when she brings focus to it (either
// programmatically, by tab index, or by clicking with the  mouse) call the custom
// makeAutoCompleteOptions(  ) method.
TextField.prototype.onChanged = function (  ) {
  this.makeAutoCompleteOptions(  );
};

TextField.prototype.onSetFocus = function (  ) {
  this.makeAutoCompleteOptions(  );
};

TextField.prototype.makeAutoCompleteOptions = function (  ) {

  // Create a copy of the array stored in the shared object for the text field with
  // the current text field's name.
  var history = TextField.so.data[this._name].concat(  );

  // If the text is not empty, find any partial matches in the history array.
  if (this.text != "") {
    for (var i = 0; i < history.length; i++) {
      // Removes any elements that don't match from the history array.
      if (history[i].indexOf(this.text) != 0) {
        history.splice(i, 1);
        i--;
      }
    }
  }

  // If the history array is undefined or has no elements, remove any existing list
  // box and exit this method.
  if (history.length == 0 || history.length == undefined) {
    this._parent.autoCompleteHistory.removeMovieClip(  );
    return;
  }

  // Create a list box and position it just underneath the text field.
  this._parent.attachMovie("FListBoxSymbol", "autoCompleteHistory", 100000);
  this._parent.autoCompleteHistory._x = this._x;
  this._parent.autoCompleteHistory._y = this._y + this._height;

  // Resize the list box to fit the width of the text field.
  this._parent.autoCompleteHistory.setSize(this._width, 50);

  // If history has fewer than three elements, shorten the list box.
  if (history.length < 3) {
    this._parent.autoCompleteHistory.setRowCount(history.length);
  }

  // Fill the list box with the elements from history and set the handler to call
  // when any changes occur to the list box (via changes to the text field's
  // contents). 
  this._parent.autoCompleteHistory.setDataProvider(history);
  this._parent.autoCompleteHistory.setChangeHandler("setValue", this);
};

// The setValue(  ) method is the change handler function for the list box.
TextField.prototype.setValue = function (lb) {

  // usingArrows indicates whether the method was called due to the user pressing the
  // arrow keys. If she used the arrow keys, exit the function because we don't want
  // to close the list box until she has actually selected a value.
  if (this.usingArrows) {
    this.usingArrows = false;
    return;
  }

  // Set the text field value to the selected value from the list box and then remove
  // the list box.
  this.text = lb.getSelectedItem(  ).label;
  lb.removeMovieClip(  );
};

TextField.prototype.onKillFocus = function (  ) {

  // Remove the list box when the text field loses focus.
  _root.autoCompletHistory.removeMovieClip(  );

  // If the text field contains no text, exit the method.
  if (this.text == "") {
    return;
  }

  // Get the array stored in the shared object for the text field. Exit this method
  // if the array already contains the text field's text value.
  var history = TextField.so.data[this._name];
  for (var i = 0; i < history.length; i++) {
    if (this.text == history[i]) {
      return;
    }
  }

  // If the shared object doesn't already have an array 
  // for this text field, create one.
  if (TextField.so.data[this._name] == undefined) {
    TextField.so.data[this._name] = new Array(  );
  }

  // Add the text field's text value to the shared object's array.
  TextField.so.data[this._name].push(this.text);
};

// Create a key listener to respond whenever keys are pressed.
keyListener = new Object(  );

keyListener.onKeyDown = function (  ) {

  // Get the current focus and create a reference to the list box.
  var focus = eval(Selection.getFocus(  ));
  var ach = focus._parent.autoCompleteHistory;

  // Get the key code for the key that has been pressed.
  var keyCode = Key.getCode(  );

  if (keyCode == 40) {
    // If the key is the down arrow, set usingArrows to true and set the selected
    // index in the list box to the next value in the list.
    focus.usingArrows = true;
    var index = (ach.getSelectedIndex(  ) == undefined) ? 
                   0 : ach.getSelectedIndex(  ) + 1;
    index = (index == ach.getLength(  )) ? 0 : index;
    ach.setSelectedIndex(index);

    // Scroll if the selected index is not visible in the list box.
    if (index > ach.getScrollPosition(  ) + 2) {
      ach.setScrollPosition(index);
    } else if (index < ach.getScrollPosition(  )) {
      ach.setScrollPosition(index);
    }
  } else if (keyCode == 38) {
    // If the key is the up arrow, do a similar thing as when the down arrow is
    // pressed, but move the selected index up instead of down.
    focus.usingArrows = true;
    var index = (ach.getSelectedIndex(  ) == undefined) ? 
                   0 : ach.getSelectedIndex(  ) - 1;
    index = (index == -1) ? ach.getLength(  ) - 1 : index;
    ach.setSelectedIndex(index);
    if (index > ach.getScrollPosition(  ) + 2) {
      ach.setScrollPosition(index);
    } else if (index < ach.getScrollPosition(  )) {
      ach.setScrollPosition(index);
    }
  } else if (keyCode == Key.ENTER) {
    // If the user has pressed the Enter key, call setValue(  ) to set the text field's
    // value and close the list box.
    focus.setValue(ach);
  } else {
    // Otherwise, if the user presses any other key, exit the function.
    return;
  }
  
  // Set the focus to the text field.
  Selection.setFocus(focus);
};

// Add the key listener to the Key object.
Key.addListener(keyListener);

To use the auto-complete feature, you only need to include AutoCompleteText.as in a Flash document that contains input text fields. Then test the movie. The text fields will automatically remember any contents you entered into them.

#include "AutoCompleteText.as"

this.createTextField("myTextField0", 1, 100, 100, 100, 20);
this.createTextField("myTextField1", 1, 100, 140, 100, 20);
    [ Team LiB ] Previous Section Next Section