Skip to content

Keyboard and Clipboard Events

Han Wei edited this page Mar 25, 2012 · 7 revisions

The quick-and-dirty: event bubbling is faked in the math DOM, keypress remembers the last keydown event and does setTimeout to check textarea contents

Keyboard event handling control flow and API

Getting keyboard events to work for normal and special keys, for both a tap and holding down, is stupidly hard. According to this excellent resource, the main hurdles that affect this project are as follows:

  • In most browsers,
    • it's only possible to reliably tell special keys (like arrow keys, backspace, tab etc) apart from text entry with the keydown event object
      • 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 on keypress
      • 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
      • we ended up waiting after keypress and seeing what text was entered into the textarea
  • Unfortunately,
    • some browsers, for special keys, only fire keydown
    • some browsers, when you hold down a key, only repeatedly fire keypress

We provide an abstraction that is cross-browser but doesn't use browser-sniffing to hide these inconsistencies from "higher-level" handlers on math DOM nodes that actually deal with text entry and backspaces and stuff.

Our cross-browser algorithm is to record keydown events and on keypress, if there's been no keydown since the last keypress of the same kind, replay the recorded one. That way special keys can be handled on keydown without worry that it doesn't fire repeatedly when you hold down special keys. Text entry is handled by checking the contents of the textarea after keypress, so there are separate higher-level math DOM event handlers for keydown and textInput.

  • The actual event handlers bound to the jQuery object (and hence, the HTML DOM element) of the root math element share a closured lastKeydn object.
    • on keydown, store the event object and set the flag before triggering keydown in the math DOM
    • on keypress, check the flag and if there's been no keydown since the last keypress, trigger keydown in the math DOM
      • now setTimeout(textInput) to wait after the keypress event has been handled by the browser to see if text has been entered into the textarea, and if so trigger textInput in the math DOM
  • The .keydown() and .textInput() event handlers inherited by all MathElements from MathElement.prototype just tail call the parent's keydown/textInput handler (Note: In the new architecture, instead of explicitly tail calling the parent's event handler, events are bubbled by the event triggerer and to cancel bubbling, the event handler explicitly returns false)
    • The root math block overrides its keydown and keypress event handlers to call the cursor's various .write() and .backspace() etc methods
    • Since the event bubbles up starting from the cursor's parent, MathElements like LatexCommandInput can override their keydown and textInput event handlers to do stuff when the cursor is in them and the user types something
      • Such MathElement key event handlers that don't want to cancel the bubbling of the event are required to explicitly fake the bubbling by calling the key event handlers of their parent MathElement
        • They can cancel the bubbling of the event by simply not calling their parent's event handlers
      • We want the same events to bubble out of the math textboxes as would bubble out of a normal textarea, so we never return false or stop propagation on any of the textarea events, but we often want to prevent the default action, so the .keydown() event handler can return false to prevent default. Like normal event handlers, returning other falsy values does not work, it has to === false

Tested (and confirmed working) on Mac for Safari 4, 5, Opera 10.5, Chrome 5+, Camino 2, Firefox 3.6, 4, even OmniWeb 5.

Tested (and confirmed working) on Windows for IE8.

Clone this wiki locally