@@ -2,6 +2,7 @@ import {polyfill as eventListenerSignalPolyfill} from './polyfills/event-listene
2
2
import { isMacOS } from './utils/user-agent.js'
3
3
import { IterateFocusableElements , iterateFocusableElements } from './utils/iterate-focusable-elements.js'
4
4
import { uniqueId } from './utils/unique-id.js'
5
+ import { isEditableElement } from './utils/is-editable-elements.js'
5
6
6
7
eventListenerSignalPolyfill ( )
7
8
@@ -267,22 +268,16 @@ function shouldIgnoreFocusHandling(keyboardEvent: KeyboardEvent, activeElement:
267
268
// its function to move focus rather than type a <TAB> character.
268
269
const keyLength = [ ...key ] . length
269
270
270
- const isTextInput =
271
- ( activeElement instanceof HTMLInputElement && activeElement . type === 'text' ) ||
272
- activeElement instanceof HTMLTextAreaElement
271
+ const isEditable = isEditableElement ( activeElement )
273
272
274
273
// If we would normally type a character into an input, ignore
275
274
// Also, Home and End keys should never affect focus when in a text input
276
- if ( isTextInput && ( keyLength === 1 || key === 'Home' || key === 'End' ) ) {
275
+ if ( isEditable && ( keyLength === 1 || key === 'Home' || key === 'End' ) ) {
277
276
return true
278
277
}
279
278
280
- // Some situations we want to ignore with <select> elements
279
+ // Some situations we specifically want to ignore with <select> elements
281
280
if ( activeElement instanceof HTMLSelectElement ) {
282
- // Regular typeable characters change the selection, so ignore those
283
- if ( keyLength === 1 ) {
284
- return true
285
- }
286
281
// On macOS, bare ArrowDown opens the select, so ignore that
287
282
if ( key === 'ArrowDown' && isMacOS ( ) && ! keyboardEvent . metaKey ) {
288
283
return true
@@ -293,16 +288,15 @@ function shouldIgnoreFocusHandling(keyboardEvent: KeyboardEvent, activeElement:
293
288
}
294
289
}
295
290
296
- // Ignore page up and page down for textareas
297
- if ( activeElement instanceof HTMLTextAreaElement && ( key === 'PageUp' || key === 'PageDown' ) ) {
298
- return true
299
- }
300
-
301
- if ( isTextInput ) {
302
- const textInput = activeElement as HTMLInputElement | HTMLTextAreaElement
303
- const cursorAtStart = textInput . selectionStart === 0 && textInput . selectionEnd === 0
291
+ if ( isEditable ) {
292
+ // An editable element might not be an input element if it is a `contenteditable` element, but it's significantly
293
+ // harder and less reliable to get the caret position for those elements, so for them we just always ignore arrows
294
+ const isInputElement = activeElement instanceof HTMLTextAreaElement || activeElement instanceof HTMLInputElement
295
+ const cursorAtStart = isInputElement && activeElement . selectionStart === 0 && activeElement . selectionEnd === 0
304
296
const cursorAtEnd =
305
- textInput . selectionStart === textInput . value . length && textInput . selectionEnd === textInput . value . length
297
+ isInputElement &&
298
+ activeElement . selectionStart === activeElement . value . length &&
299
+ activeElement . selectionEnd === activeElement . value . length
306
300
307
301
// When in a text area or text input, only move focus left/right if at beginning/end of the field
308
302
if ( key === 'ArrowLeft' && ! cursorAtStart ) {
@@ -312,8 +306,15 @@ function shouldIgnoreFocusHandling(keyboardEvent: KeyboardEvent, activeElement:
312
306
return true
313
307
}
314
308
315
- // When in a text area, only move focus up/down if at beginning/end of the field
316
- if ( textInput instanceof HTMLTextAreaElement ) {
309
+ const isContentEditable = activeElement instanceof HTMLElement && activeElement . isContentEditable
310
+
311
+ // When in multiline inputs
312
+ if ( activeElement instanceof HTMLTextAreaElement || isContentEditable ) {
313
+ // Always ignore page up/down
314
+ if ( key === 'PageUp' || key === 'PageDown' ) {
315
+ return true
316
+ }
317
+ // Only move focus up/down if at beginning/end of the field
317
318
if ( key === 'ArrowUp' && ! cursorAtStart ) {
318
319
return true
319
320
}
0 commit comments