Friday, November 26, 2010

Autocomplete and JavaScript Change Event

Different Sorts of Autocomplete

When it comes to autocomplete in browsers, you need to make distinction between autocomplete for:

  • Username/password — Once you have a username and password saved for a certain login form, the browser will pre-fill the username and password fields with the saved values.
  • General form fields — Most browsers, including Firefox, IE, and Chrome, provides a similar autocomplete interface for form fields: as you start typing in a field, the browser will provide a list of suggestions. If you select one, the value you select will be used to populate the field. (Chrome might, based on the choice you made, also populate other related fields.)

Typical autocomplete UI

The HTML Specification

When it comes to DOM event, and in particular the change event, there is a significant difference between those two cases: username/password fields are auto-filled immediately when the page is shown; for other fields, you explicitly select a value from a list and tab to another field. This is important in the context of the HTML specification. The HTML 4.01 specification says:

The onchange event occurs when a control loses the input focus and its value has been modified since gaining focus.

My interpretation is that browsers should dispatch the change event when users select a value from a list of suggestions, but not when browsers pre-fill the username and password fields, as in that case the username and password fields don't gain and loose the focus. HTML 5 is more abstract (emphasis mine):

When the change event applies, if the element does not have an activation behavior defined but uses a user interface that involves an explicit commit action, then any time the user commits a change to the element's value or list of selected files, the user agent must queue a task to fire a simple event that bubbles named change at the input element, then broadcast formchange events at the input element's form owner.

HTML 4.01 talks about dispatching the change event when the form field looses the focus, which makes sense for text fields, but doesn't for file selection fields (<input type="file">): in that case it make more sense to dispatch the change event when the file has been selected, rather than wait for users to change the focus to another field. This is most likely why HTML5 talks about "users commiting the change" rather than "the control loosing the focus".

Even with this HTML5 definition of the change event, for the case of the username/password fields pre-filled by browsers as the page is loaded, I find it hard to argue that users "committed a change" by just loading the page. So based on my interpretation of the HTML 4.01 and HTML 5 specifications, I would expect browsers to:

  • Dispatch the change event when users select a suggested value and tab to another field.
  • Not dispatch the change event to fields they pre-filled without users tabbing through the fields, such as the username/password fields.

The Real World

Expectations are rarely in line with reality, especially when it comes to browser behavior:

  • For username/password fields:

    • Firefox 4, IE 7, and IE 8 don't dispatch the change event.
    • Safari 5 and Chrome 9 do dispatch the change event.
  • For other form fields:

    • IE 7 and IE 8 don't dispatch the change event.
    • Firefox 4 does dispatch the change change event when users select a value from a list of suggestions and tab out of the field.
    • Chrome 9 does not dispatch the change event.
    • Safari 5 does dispatch the change event.

You can test this by:

  • For username/password fields, load the login test case. Enter a username/password, submit the form, tell the browser to remember the login, reload the form. On Firefox, Chrome, and Safari the password field comes pre-filled, but the change event is dispatched only by Chrome and Safari. On IE, the password will be pre-filled only after you enter the username, but even as you tab through the password field, no change event is dispatched.
  • For other form fields, use this test case.

Workarounds

The bottom line is that your JavaScript code can't rely on the change event. If your code needs to know when the value of a field changes, you have two choices:

  1. Have some JavaScript code that runs at a regular interval, checks the value of all the fields, and calls your change handler when it notices that a value has changed.
  2. Disable autocomplete by adding autocomplete="off" on the <form> element. This technique has been supported by browsers since IE 5 (March 1999) and Netscape 6.2 (October 2001), and now made its way into HTML5.