@zoijs/core 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@ All notable changes to Zoijs are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/), and Zoijs follows
5
5
  [Semantic Versioning](https://semver.org/) (see `VERSIONING.md`).
6
6
 
7
+ ## [1.3.2] — 2026-06-26
8
+
9
+ ### Fixed
10
+ - **Focus is preserved across a keyed reorder.** The 1.3.1 minimal-move change can
11
+ move the subtree that holds the focused element, which blurs it in browsers.
12
+ `each` now captures focus + caret position before reordering and restores them
13
+ after, so reordering a list never steals focus or selection — whichever nodes
14
+ happen to move. Verified in Chromium, Firefox, and WebKit
15
+ (`browser-tests/regression.spec.js`).
16
+
7
17
  ## [1.3.1] — 2026-06-26
8
18
 
9
19
  ### Performance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zoijs/core",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "Zoijs — a beginner-friendly, no-build, no-Virtual-DOM frontend framework. Plain HTML, CSS, and JavaScript.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -343,6 +343,23 @@ function setupKeyedList(anchor, marker) {
343
343
  for (const n of rec.nodes) n.remove();
344
344
  }
345
345
 
346
+ // Moving the subtree that holds the focused element blurs it in some browsers,
347
+ // so capture focus (and caret) before the moves and restore it after — a
348
+ // reorder must never steal focus or selection, whichever nodes happen to move.
349
+ const doc = anchor.ownerDocument;
350
+ const active = doc && doc.activeElement;
351
+ const refocus = active && active !== doc.body && parent.contains(active);
352
+ let selStart = null;
353
+ let selEnd = null;
354
+ if (refocus) {
355
+ try {
356
+ selStart = active.selectionStart;
357
+ selEnd = active.selectionEnd;
358
+ } catch {
359
+ selStart = null; // not a text field — focus only, no caret to restore
360
+ }
361
+ }
362
+
346
363
  // Items in the longest increasing subsequence of old positions are already in
347
364
  // the right relative order — leave them put (minimal DOM moves). Everything
348
365
  // else (moved or new) is inserted before the running cursor, walking from the
@@ -362,6 +379,17 @@ function setupKeyedList(anchor, marker) {
362
379
  cursor = rec.nodes[0] || cursor;
363
380
  }
364
381
 
382
+ if (refocus && doc.activeElement !== active) {
383
+ active.focus();
384
+ if (selStart !== null && active.setSelectionRange) {
385
+ try {
386
+ active.setSelectionRange(selStart, selEnd);
387
+ } catch {
388
+ /* element no longer supports selection — focus alone is enough */
389
+ }
390
+ }
391
+ }
392
+
365
393
  records = newRecords;
366
394
  currentList = ordered;
367
395
  };