Skip to content
Han Wei edited this page Jul 24, 2012 · 3 revisions

This is mainly about v0.3, as of this writing the dev branch of MathQuill.

textarea.js is our new pain-free cross-browser keyboard and clipboard events normalization library, isolated from the rest of MathQuill. It uses no browser sniffing and normalizes both the data and the timing.

It provides "textarea manager", an abstraction layer wrapping the textarea in an object with methods to manipulate and listen to events on, that hides all the nasty cross- browser incompatibilities behind a uniform API.

Design goal: This is a hard internal abstraction barrier. Cross-browser inconsistencies are not allowed to leak through and be dealt with by event handlers. All future cross-browser issues that arise must be deal with here, and if necessary, the API updated.

According to this excellent resource and our own testing, the keyboard events browser inconsistencies affecting this project are as follows:

  • In most browsers,
    • only on keydown is it possible to preventDefault(), and only with the keydown event object is it possible to reliably tell special keys (like arrow keys, backspace, tab etc.) apart from text entry
      • e.g. on keypress the keycodes for the left, up, right and down arrow keys are identical to that of %, &, ' and (, respectively
    • but it's only possible to reliably tell what character was typed by checking the textarea immediately after the keypress event
      • in fact even the jQuery-normalized event.which on keypress is unreliable, for example on the French keyboard the backslash \ (crucial to MathQuill) is typed with Ctrl+Alt+8, which for all a web app can tell is a browser or OS shortcut #11
  • Unfortunately,
    • some browsers, for special keys, only fire keydown
      • (WebKit, Internet Explorer)
    • some browsers, when you hold down a key, only repeatedly fire keypress

Here's a state diagram of the event handlers' logic: (Click to enlarge)

This is simpler than it looks, there's a lot of duplication because the algorithm is basically being described casewise, but it boils down to very little code.

To be a bit more verbose, consider the following 4 states that the textarea could be in:

  • Empty: textarea.val() === ''
  • Char Typed: Possibly containing a single character that was just typed, or else empty
  • Paste: Possibly containing text that was just pasted in, or else empty
  • Selection: Possibly containing a selection, or else empty

In each of these states, it's possible for a keydown, keypress, or paste event to fire, for the select method of the textarea manager to be called, and in the case of Paste or Char Typed, for a timeout to fire.

Note that with a few exceptions described below (but not shown in the diagram),

  • a keydown event always ends with the textarea in the Empty state,
  • a keypress event always ends in the Char Typed state,
  • a paste event always ends in the Paste state,
  • a select method call always ends in the Selection state,
  • and a timeout firing always ends in the Empty state.

For the events, we bind event handlers that get called immediately before the change to the textarea actually happens. For the select method and the timeout, we define them, so we can do whatever we want before actually selecting or emptying the textarea.

  • From Empty,
    • on keydown, the textarea stays Empty, just want to call the keyCallback
    • immediately after keypress, a Char is Typed in the textarea
    • immediately after paste, text is Pasted into the textarea
    • when select() is called, the textarea is set to contain the Selection passed as an argument
Clone this wiki locally