Skip to content

Commit 92f8b46

Browse files
committed
add test, use focusin/focusout rather than focus/blur
1 parent 2cd2646 commit 92f8b46

File tree

4 files changed

+35
-15
lines changed

4 files changed

+35
-15
lines changed
Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
1-
import { render_effect } from '../../../reactivity/effects.js';
1+
import { listen } from './shared.js';
22

33
/**
44
* @param {(activeElement: Element | null) => void} update
55
* @returns {void}
66
*/
77
export function bind_active_element(update) {
8-
var handler = () => {
9-
update(document.activeElement);
10-
};
11-
12-
handler();
8+
listen(document, ['focusin', 'focusout'], (event) => {
9+
if (event && event.type === 'focusout' && /** @type {FocusEvent} */ (event).relatedTarget) {
10+
// The tests still pass if we remove this, because of JSDOM limitations, but it is necessary
11+
// to avoid temporarily resetting to `document.body`
12+
return;
13+
}
1314

14-
document.addEventListener('focus', handler, true);
15-
document.addEventListener('blur', handler, true);
16-
17-
render_effect(() => {
18-
return () => {
19-
document.removeEventListener('focus', handler);
20-
document.removeEventListener('blur', handler);
21-
};
15+
update(document.activeElement);
2216
});
2317
}

packages/svelte/src/internal/client/dom/elements/bindings/shared.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { add_form_reset_listener } from '../misc.js';
66
* then listens to the given events until the render effect context is destroyed
77
* @param {EventTarget} target
88
* @param {Array<string>} events
9-
* @param {() => void} handler
9+
* @param {(event?: Event) => void} handler
1010
* @param {any} call_handler_immediately
1111
*/
1212
export function listen(target, events, handler, call_handler_immediately = true) {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
// This test is slightly inaccurate, because blurring elements (or focusing the `<body>` directly)
5+
// doesn't trigger the relevant `focusin` event in JSDOM.
6+
export default test({
7+
test({ assert, target, logs }) {
8+
const [btn1, btn2] = target.querySelectorAll('button');
9+
10+
flushSync(() => btn1.focus());
11+
assert.deepEqual(logs, ['...', 'BODY', 'one']);
12+
13+
flushSync(() => btn2.focus());
14+
assert.deepEqual(logs, ['...', 'BODY', 'one', 'two']);
15+
}
16+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script>
2+
let active;
3+
4+
$: console.log(active?.id || active?.nodeName || '...');
5+
</script>
6+
7+
<svelte:document bind:activeElement={active} />
8+
9+
<button id="one">one</button>
10+
<button id="two">two</button>

0 commit comments

Comments
 (0)