-
Notifications
You must be signed in to change notification settings - Fork 19
textarea.js
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 topreventDefault()
, and only with thekeydown
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
- e.g. on
- 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
onkeypress
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
- in fact even the jQuery-normalized
- only on
- Unfortunately,
- some browsers, for special keys, only fire
keydown
- (WebKit, Internet Explorer)
- some browsers, when you hold down a key, only repeatedly fire
keypress
- (Opera, Firefox 3.6 and earlier)
- some browsers, for special keys, only fire
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 thekeyCallback
- 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
- on