diff --git a/src/event/behavior/keydown.ts b/src/event/behavior/keydown.ts
index e0d5b9be..6008399c 100644
--- a/src/event/behavior/keydown.ts
+++ b/src/event/behavior/keydown.ts
@@ -101,7 +101,7 @@ const keydownBehavior: {
Tab: (event, target, instance) => {
return () => {
const dest = getTabDestination(
- target,
+ document.activeElement ?? target,
instance.system.keyboard.modifiers.Shift,
)
focusElement(dest)
diff --git a/tests/event/behavior/keydown.ts b/tests/event/behavior/keydown.ts
index da6f10dc..7f1d39a3 100644
--- a/tests/event/behavior/keydown.ts
+++ b/tests/event/behavior/keydown.ts
@@ -303,6 +303,51 @@ cases(
},
)
+cases(
+ 'tab from a target moved during the keyboard event',
+ ({focus, shiftKey = false, expectedFocus, expectedSelection}) => {
+ const {xpathNode} = render(
+ ``,
+ {
+ focus,
+ },
+ )
+
+ const instance = setupInstance()
+ instance.system.keyboard.modifiers.Shift = shiftKey
+
+ document.activeElement?.addEventListener('keydown', (e) => {
+ xpathNode('button[1]').focus()
+ })
+ instance.dispatchUIEvent(document.activeElement as Element, 'keydown', {
+ key: 'Tab',
+ })
+ expect(xpathNode(expectedFocus)).toHaveFocus()
+ if (expectedSelection) {
+ expect(getUISelection(xpathNode(expectedFocus))).toEqual(
+ expect.objectContaining(expectedSelection),
+ )
+ }
+ },
+ {
+ 'tab to input2': {
+ focus: '//body',
+ expectedFocus: 'input[2]',
+ expectedSelection: {startOffset: 0, endOffset: 4},
+ },
+ 'tab to number input': {
+ focus: 'input[1]',
+ expectedFocus: 'input[2]',
+ expectedSelection: {startOffset: 0, endOffset: 4},
+ },
+ 'tab backward to input1': {
+ focus: 'input[2]',
+ shiftKey: true,
+ expectedFocus: 'input[1]',
+ },
+ },
+)
+
cases(
'walk through radio group per arrow keys',
({focus, key, expectedTarget}) => {