Skip to content

Commit 88a0138

Browse files
authored
Fix incorrectly focused Combobox.Input component on page load (#2654)
* only call `setSelectionRange` when the input is the focused input * update changelog
1 parent cc163ea commit 88a0138

File tree

4 files changed

+18
-2
lines changed

4 files changed

+18
-2
lines changed

packages/@headlessui-react/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- Render `<MainTreeNode />` in `Popover.Group` component only ([#2634](https://github.com/tailwindlabs/headlessui/pull/2634))
1414
- Disable smooth scrolling when opening/closing `Dialog` components on iOS ([#2635](https://github.com/tailwindlabs/headlessui/pull/2635))
1515
- Don't assume `<Tab />` components are available when setting the next index ([#2642](https://github.com/tailwindlabs/headlessui/pull/2642))
16+
- Fix incorrectly focused `Combobox.Input` component on page load ([#2654](https://github.com/tailwindlabs/headlessui/pull/2654))
1617

1718
## [1.7.16] - 2023-07-27
1819

packages/@headlessui-react/src/components/combobox/combobox.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import { useControllable } from '../../hooks/use-controllable'
5353
import { useWatch } from '../../hooks/use-watch'
5454
import { useTrackedPointer } from '../../hooks/use-tracked-pointer'
5555
import { isMobile } from '../../utils/platform'
56+
import { useOwnerDocument } from '../../hooks/use-owner'
5657

5758
enum ComboboxState {
5859
Open,
@@ -738,6 +739,7 @@ function InputFn<
738739
let actions = useActions('Combobox.Input')
739740

740741
let inputRef = useSyncRefs(data.inputRef, ref)
742+
let ownerDocument = useOwnerDocument(data.inputRef)
741743

742744
let isTyping = useRef(false)
743745

@@ -799,6 +801,11 @@ function InputFn<
799801
if (isTyping.current) return
800802
if (!input) return
801803

804+
// Bail when the input is not the currently focused element. When it is not the focused
805+
// element, and we call the `setSelectionRange`, then it will become the focused
806+
// element which may be unwanted.
807+
if (ownerDocument?.activeElement !== input) return
808+
802809
let { selectionStart, selectionEnd } = input
803810

804811
// A custom selection is used, no need to move the caret
@@ -811,7 +818,7 @@ function InputFn<
811818
input.setSelectionRange(input.value.length, input.value.length)
812819
})
813820
},
814-
[currentDisplayValue, data.comboboxState]
821+
[currentDisplayValue, data.comboboxState, ownerDocument]
815822
)
816823

817824
// Trick VoiceOver in behaving a little bit better. Manually "resetting" the input makes VoiceOver

packages/@headlessui-vue/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Disable smooth scrolling when opening/closing `Dialog` components on iOS ([#2635](https://github.com/tailwindlabs/headlessui/pull/2635))
1616
- Don't assume `<Tab />` components are available when setting the next index ([#2642](https://github.com/tailwindlabs/headlessui/pull/2642))
1717
- Improve SSR of the `Disclosure` component ([#2645](https://github.com/tailwindlabs/headlessui/pull/2645))
18+
- Fix incorrectly focused `ComboboxInput` component on page load ([#2654](https://github.com/tailwindlabs/headlessui/pull/2654))
1819

1920
## [1.7.15] - 2023-07-27
2021

packages/@headlessui-vue/src/components/combobox/combobox.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { useControllable } from '../../hooks/use-controllable'
3838
import { useTrackedPointer } from '../../hooks/use-tracked-pointer'
3939
import { isMobile } from '../../utils/platform'
4040
import { disposables } from '../../utils/disposables'
41+
import { getOwnerDocument } from '../../utils/owner'
4142

4243
function defaultComparator<T>(a: T, z: T): boolean {
4344
return a === z
@@ -700,6 +701,7 @@ export let ComboboxInput = defineComponent({
700701
},
701702
setup(props, { emit, attrs, slots, expose }) {
702703
let api = useComboboxContext('ComboboxInput')
704+
let ownerDocument = computed(() => getOwnerDocument(dom(api.inputRef)))
703705

704706
let isTyping = { value: false }
705707

@@ -743,7 +745,7 @@ export let ComboboxInput = defineComponent({
743745
// - By pressing `escape`
744746
// - By clicking `outside` of the Combobox
745747
watch(
746-
[currentDisplayValue, api.comboboxState],
748+
[currentDisplayValue, api.comboboxState, ownerDocument],
747749
([currentDisplayValue, state], [oldCurrentDisplayValue, oldState]) => {
748750
// When the user is typing, we want to not touch the `input` at all. Especially when they
749751
// are using an IME, we don't want to mess with the input at all.
@@ -766,6 +768,11 @@ export let ComboboxInput = defineComponent({
766768
if (isTyping.value) return
767769
if (!input) return
768770

771+
// Bail when the input is not the currently focused element. When it is not the focused
772+
// element, and we call the `setSelectionRange`, then it will become the focused
773+
// element which may be unwanted.
774+
if (ownerDocument.value?.activeElement !== input) return
775+
769776
let { selectionStart, selectionEnd } = input
770777

771778
// A custom selection is used, no need to move the caret

0 commit comments

Comments
 (0)