diff --git a/docs/Config.md b/docs/Config.md index 4bceb13ec..17c1457da 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -121,6 +121,12 @@ You can also specify a speech-friendly representation of the operator name by su `substituteTextarea` is a function that creates a focusable DOM element that is called when setting up a math field. Overwriting this may be useful for hacks like suppressing built-in virtual keyboards. It defaults to ``. For example, [Desmos](https://www.desmos.com/calculator) substitutes `` on iOS to suppress the built-in virtual keyboard in favor of a custom math keypad that calls the MathQuill API. Unfortunately there's no universal [check for a virtual keyboard](http://stackoverflow.com/q/2593139/362030) or [way to detect a touchscreen](http://www.stucox.com/blog/you-cant-detect-a-touchscreen/), and even if you could, a touchscreen ≠ virtual keyboard (Windows 8 and ChromeOS devices have both physical keyboards and touchscreens and iOS and Android devices can have Bluetooth keyboards). Desmos currently sniffs the user agent for iOS, so Bluetooth keyboards just don't work in Desmos on iOS. The tradeoffs are up to you. +## tabbable + +For static and editable math fields, when `tabbable` is false, the math field is not part of the page's tab order. Despite that, the math field can still be focused when selected by a mouse. + +Static math fields default to `tabbable: false`, Editable math fields default to `tabbable:true`. + # Handlers Handlers are called after a specified event. They are called directly on the `handlers` object passed in, preserving the `this` value, so you can do stuff like: diff --git a/src/mathquill.d.ts b/src/mathquill.d.ts index c06753827..5552c258c 100644 --- a/src/mathquill.d.ts +++ b/src/mathquill.d.ts @@ -33,6 +33,8 @@ declare namespace MathQuill { html: () => string; mathspeak: () => string; text(): string; + blur: () => void; + focus: () => void; } interface EditableMathQuill { @@ -120,6 +122,7 @@ declare namespace MathQuill { typingSlashWritesDivisionSymbol?: boolean; typingPercentWritesPercentOf?: boolean; resetCursorOnBlur?: boolean | undefined; + tabbable?: boolean; leftRightIntoCmdGoes?: 'up' | 'down'; enableDigitGrouping?: boolean; tripleDotsAreEllipsis?: boolean; @@ -178,6 +181,8 @@ declare namespace MathQuill { html: () => string; mathspeak: () => string; text(): string; + blur: () => void; + focus: () => void; } interface EditableMathQuill extends BaseMathQuill { @@ -188,8 +193,6 @@ declare namespace MathQuill { keystroke: (key: string, evt?: KeyboardEvent) => void; typedText: (text: string) => void; clearSelection: () => void; - blur: () => void; - focus: () => void; getAriaPostLabel: () => string; setAriaPostLabel: (str: string, timeout?: number) => void; ignoreNextMousedown: (func: () => boolean) => void; diff --git a/src/publicapi.ts b/src/publicapi.ts index 1e29dd9dd..f40384ce1 100644 --- a/src/publicapi.ts +++ b/src/publicapi.ts @@ -88,7 +88,7 @@ class Options { constructor(public version: 1 | 2 | 3) {} ignoreNextMousedown: (_el: MouseEvent) => boolean; - substituteTextarea: () => HTMLElement; + substituteTextarea: (tabbable?: boolean) => HTMLElement; /** Only used in interface versions 1 and 2. */ substituteKeyboardEvents: SubstituteKeyboardEvents; @@ -106,6 +106,7 @@ class Options { leftRightIntoCmdGoes?: 'up' | 'down'; enableDigitGrouping?: boolean; tripleDotsAreEllipsis?: boolean; + tabbable?: boolean; mouseEvents?: boolean; maxDepth?: number; disableCopyPaste?: boolean; @@ -334,14 +335,6 @@ function getInterface(v: number): MathQuill.v3.API | MathQuill.v1.API { selection() { return this.__controller.exportLatexSelection(); } - select() { - this.__controller.selectAll(); - return this; - } - clearSelection() { - this.__controller.cursor.clearSelection(); - return this; - } html() { return this.__controller.root .domFrag() @@ -358,6 +351,15 @@ function getInterface(v: number): MathQuill.v3.API | MathQuill.v1.API { }); return this; } + focus() { + this.__controller.getTextareaOrThrow().focus(); + if (this.__controller.editable) this.__controller.scrollHoriz(); + return this; + } + blur() { + this.__controller.getTextareaOrThrow().blur(); + return this; + } } abstract class EditableField @@ -371,15 +373,15 @@ function getInterface(v: number): MathQuill.v3.API | MathQuill.v1.API { this.__controller.editablesTextareaEvents(); return this; } - focus() { - this.__controller.getTextareaOrThrow().focus(); - this.__controller.scrollHoriz(); + select() { + this.__controller.selectAll(); return this; } - blur() { - this.__controller.getTextareaOrThrow().blur(); + clearSelection() { + this.__controller.cursor.clearSelection(); return this; } + write(latex: string) { this.__controller.writeLatex(latex); this.__controller.scrollHoriz(); diff --git a/src/services/focusBlur.ts b/src/services/focusBlur.ts index 6f39d013f..4d75ae5c2 100644 --- a/src/services/focusBlur.ts +++ b/src/services/focusBlur.ts @@ -71,18 +71,14 @@ class Controller_focusBlur extends Controller_exportText { }; private handleTextareaFocusStatic = () => { + if (!this.cursor.selection || this.cursor.selection.isCleared()) { + this.cursor.controller.selectAll(); + } this.blurred = false; }; private handleTextareaBlurStatic = () => { - if (this.cursor.selection) { - this.cursor.selection.clear(); - } - //detaching during blur explodes in WebKit - setTimeout(() => { - domFrag(this.getTextareaSpanOrThrow()).detach(); - this.blurred = true; - }); + this.cursor.selection?.clear(); }; private handleWindowBlur = () => { diff --git a/src/services/mouse.ts b/src/services/mouse.ts index efe8acf7a..c3acf4a64 100644 --- a/src/services/mouse.ts +++ b/src/services/mouse.ts @@ -47,7 +47,6 @@ class Controller_mouse extends Controller_latex { var ctrlr = root.controller, cursor = ctrlr.cursor, blink = cursor.blink; - var textareaSpan = ctrlr.getTextareaSpanOrThrow(); var textarea = ctrlr.getTextareaOrThrow(); e.preventDefault(); // doesn't work in IE≤8, but it's a one-line fix: @@ -91,8 +90,6 @@ class Controller_mouse extends Controller_latex { if (ctrlr.editable) { cursor.show(); cursor.controller.aria.queue(cursor.parent).alert(); - } else { - domFrag(textareaSpan).detach(); } } @@ -118,9 +115,6 @@ class Controller_mouse extends Controller_latex { }; if (ctrlr.blurred) { - if (rootElement && !ctrlr.editable) { - domFrag(rootElement).prepend(domFrag(textareaSpan)); - } textarea.focus(); // focus call may bubble to clients, who may then write to // mathquill, triggering cancelSelectionOnEdit. If that happens, we diff --git a/src/services/textarea.ts b/src/services/textarea.ts index 436c7cf2d..6348e3460 100644 --- a/src/services/textarea.ts +++ b/src/services/textarea.ts @@ -2,13 +2,14 @@ * Manage the MathQuill instance's textarea * (as owned by the Controller) ********************************************/ -Options.prototype.substituteTextarea = function () { +Options.prototype.substituteTextarea = function (tabbable?: boolean) { return h('textarea', { autocapitalize: 'off', autocomplete: 'off', autocorrect: 'off', spellcheck: false, - 'x-palm-disable-ste-all': true + 'x-palm-disable-ste-all': true, + tabindex: tabbable ? undefined : '-1' }); }; function defaultSubstituteKeyboardEvents(jq: $, controller: Controller) { @@ -21,7 +22,13 @@ class Controller extends Controller_scrollHoriz { createTextarea() { this.textareaSpan = h('span', { class: 'mq-textarea' }); - const textarea = this.options.substituteTextarea(); + + const tabbable = + this.options.tabbable !== undefined + ? this.options.tabbable + : this.KIND_OF_MQ !== 'StaticMath'; + + const textarea = this.options.substituteTextarea(tabbable); if (!textarea.nodeType) { throw 'substituteTextarea() must return a DOM element, got ' + textarea; } @@ -180,6 +187,7 @@ class Controller extends Controller_scrollHoriz { setupStaticField() { this.mathspeakSpan = h('span', { class: 'mq-mathspeak' }); domFrag(this.container).prepend(domFrag(this.mathspeakSpan)); + domFrag(this.container).prepend(domFrag(this.textareaSpan)); this.updateMathspeak(); this.blurred = true; this.cursor.hide().parent.blur(this.cursor); diff --git a/test/basic.html b/test/basic.html index 67ad1b0ea..846743bba 100644 --- a/test/basic.html +++ b/test/basic.html @@ -46,6 +46,7 @@
+ On the other hand, you can make static math tabbable to appear in the + tab order despite being non-editable. The entire range is selected when + tabbed into: + 1.234\times 10^{8}. +
+Note that if you're only rendering static math, MathJax supports more of LaTeX and @@ -118,7 +125,7 @@
In many applications, such as a chat client, you probably type mostly normal text with some math interspersed, so there is also a MathQuill - textbox that let's you type math between $'s: + textbox that lets you type math between $'s: The Quadratic Equation is $x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}$ @@ -161,6 +168,9 @@