Skip to content

Commit

Permalink
Simplifying workaround for webkit focus selection
Browse files Browse the repository at this point in the history
  • Loading branch information
tbiethman committed Aug 24, 2022
1 parent f082b2d commit 47d1155
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,8 @@ describe('src/cy/commands/actions/type - #type special chars', () => {
attachKeyListeners({ input })

cy.get(':text:first').invoke('val', 'ab')
.focus()
.then(($input) => $input[0].setSelectionRange(0, 0))
.focus()
.type('{backspace}')
.should('have.value', 'ab')

Expand Down Expand Up @@ -357,8 +357,8 @@ describe('src/cy/commands/actions/type - #type special chars', () => {
attachKeyListeners({ input })

cy.get('textarea:first').invoke('val', 'ab')
.focus()
.then(($textarea) => $textarea[0].setSelectionRange(0, 0))
.focus()
.type('{backspace}')
.should('have.value', 'ab')

Expand Down Expand Up @@ -452,8 +452,8 @@ describe('src/cy/commands/actions/type - #type special chars', () => {
attachKeyListeners({ input })

cy.get(':text:first').invoke('val', 'ab')
.focus()
.then(($input) => $input[0].setSelectionRange(0, 0))
.focus()
.type('{del}')
.should('have.value', 'b')

Expand All @@ -467,7 +467,6 @@ describe('src/cy/commands/actions/type - #type special chars', () => {
attachKeyListeners({ input })

cy.get(':text:first').invoke('val', 'ab')

.then(($input) => $input[0].setSelectionRange(0, 0))
.focus()
.type('{selectall}{del}')
Expand Down Expand Up @@ -497,8 +496,8 @@ describe('src/cy/commands/actions/type - #type special chars', () => {
attachKeyListeners({ textarea })

cy.get('textarea:first').invoke('val', 'ab')
.focus()
.then(($textarea) => $textarea[0].setSelectionRange(0, 0))
.focus()
.type('{del}')
.should('have.value', 'b')

Expand Down
13 changes: 0 additions & 13 deletions packages/driver/src/cy/commands/actions/focus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import $dom from '../../../dom'
import $utils from '../../../cypress/utils'
import $errUtils from '../../../cypress/error_utils'
import $elements from '../../../dom/elements'
import $selection from '../../../dom/selection'
import type { Log } from '../../../cypress/log'

interface InternalFocusOptions extends Partial<Cypress.Loggable & Cypress.Timeoutable> {
Expand Down Expand Up @@ -90,18 +89,6 @@ export default (Commands, Cypress, cy) => {

cy.fireFocus(el)

if (Cypress.isBrowser('webkit') && (
$elements.isInput(el) || $elements.isTextarea(el)
)) {
// Force selection to end in WebKit, unless selection
// has been set by user.
// It's a curried function, so the 2 arguments are valid.
// @ts-ignore
$selection.moveSelectionToEnd(el, {
onlyIfEmptySelection: true,
})
}

const verifyAssertions = () => {
return cy.verifyUpcomingAssertions(options.$el, options, {
onRetry: verifyAssertions,
Expand Down
48 changes: 11 additions & 37 deletions packages/driver/src/cy/focused.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,12 @@ export const create = (state: StateFunc) => ({
const $focused = this.getFocused(el.ownerDocument)

let hasFocused = false
let onFocusCapture

// we need to bind to the focus event here
// because some browsers will not ever fire
// the focus event if the window itself is not
// currently focused
const cleanup = () => {
if (onFocusCapture) {
$elements.callNativeMethod(el, 'removeEventListener', 'focus', onFocusCapture, { capture: true })
}

return $elements.callNativeMethod(el, 'removeEventListener', 'focus', onFocus)
}

Expand All @@ -142,45 +137,24 @@ export const create = (state: StateFunc) => ({
}

if (Cypress.isBrowser('webkit')) {
// By default, WebKit will select the contents of an input element when the input is focused.
// By default, WebKit will select the entire contents of an input element when the input is focused.
// This is problematic, as we use the existence of any selection to determine whether
// we adjust the input's cursor and prepare the input for receiving additional content.
// Without intervention, we will always interpret this default selection as a user-performed selection
// and persist it, leaving the selection contents to be overwritten rather than appended to
// on subsequent actions.
//
// In order to avoid this behavior, we use a focus event during the capture phase to set
// our own initial blank selection. This short-circuits WebKit's default behavior and ensures
// that any user-performed selections performed during the focus event's bubble phase are still applied.

onFocusCapture = (event: FocusEvent) => {
const eventTarget = event.currentTarget as HTMLInputElement

if (!eventTarget.setSelectionRange) {
return
}

try {
// Prior to being focused, the element's selectionStart/End will be at 0.
// Even so, we need to explicitly call setSelectionRange here to prevent WebKit
// from selecting the contents after being focused.
//
// By re-setting the selection at the current start/end values,
// we ensure that any selection values set by previous event handlers
// are persisted.
eventTarget.setSelectionRange(
eventTarget.selectionStart,
eventTarget.selectionEnd,
)
} catch (e) {
// Some input types do not support selections and will throw when
// setSelectionRange is called. We can ignore these errors,
// as these elements wouldn't have a selection to we need to
// prevent anyway.
}
// In order to avoid this behavior, we explicitly set the selection range with the current
// selectionStart/selectionEnd values. This will bypass the default select-all behavior, even though
// logically it looks like a no-op.
try {
el.setSelectionRange(el.selectionStart, el.selectionEnd)
} catch (e) {
// Some input types do not support selections and will throw when
// setSelectionRange is called. We can ignore these errors,
// as these elements wouldn't have a selection to we need to
// prevent anyway.
}

$elements.callNativeMethod(el, 'addEventListener', 'focus', onFocusCapture, { capture: true })
}

$elements.callNativeMethod(el, 'addEventListener', 'focus', onFocus)
Expand Down

0 comments on commit 47d1155

Please sign in to comment.