Skip to content

fix(ui): keep filter row alive during table rebuild so typing never loses focus (rant 2026-08-25T11:15:16) - #148

Merged
argszero merged 1 commit into
mainfrom
fix/tx-filter-focus
Aug 25, 2026
Merged

fix(ui): keep filter row alive during table rebuild so typing never loses focus (rant 2026-08-25T11:15:16)#148
argszero merged 1 commit into
mainfrom
fix/tx-filter-focus

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fix a regression from PR #146 (v0.7.18 server-side column filters): typing into a column-filter input on the transactions page lost focus after every keystroke, making it impossible to type a full word (e.g. "sh" — after "s" the input loses focus).

Root cause: input debounce callback → renderTransactions detects the filter-signature change → loadTransactions() async fetch → after fetch returns, renderTransactionsbuildDataTable executes container.innerHTML = html (full table rebuild) → the old filter input is destroyed, focus lost. The focus-restore code ran after the render that triggered the fetch, not after the DOM rebuild that follows the fetch return.

Fix (host-confirmed option B): the header filter row is separated from the full-table rebuild — buildDataTable now renders the thead (sort buttons + filter row) only once per container and rebuilds only the tbody + pager on every render, so the filter input DOM is never destroyed and focus is naturally preserved.

Related Issue

Rant 2026-08-25T11:15:16 (verbatim):

交易记录页列筛选输入框:每输入一个字母就触发刷新并失去焦点,无法完整输入一个单词(如想输入 "sh",输入 "s" 后焦点丢失)。v0.7.18 服务端列筛选改造(PR #146)后的回归。

【修复方案(宿主确认:只做 B)】重建表格时不动表头筛选行——把筛选输入行从整表重建中分离(独立容器/独立 innerHTML),只重建数据行与分页器;输入框 DOM 永不销毁,焦点天然保留。

Changes

  • ui/js/app.js:
    • tableTheadHtml() — thead (sort buttons + filter row) rendered once, only when the container has no <table> yet; input values seeded from state.filters on first render, afterwards the inputs are the DOM source of truth
    • tableBodyHtml() — data rows, rebuilt on every render
    • buildDataTable() — rebuilds only tbody + pager; container-level one-time event delegation (sort / filter input / pager / page-size / IME) reads the latest config via container._dt, so events survive rebuilds and are never lost after the first keystroke
    • IME composition tracking (compositionstart/compositionend + focusout safety): no table rebuild and no setSelectionRange while composing; a refresh is debounced after compositionend — Chinese IME composition is not interrupted
    • focus restore kept only as a fallback when the input is not already focused (no cursor jumps while typing)
  • ui/index.html: cache-bust bumped to 20260825-2

Tests

  • node --check ui/js/app.js — OK
  • cargo test — 147/147 pass
  • Manual reasoning of the full event flow (debounce → filter-sig reload → tbody-only rebuild → focus preserved)

Checklist

  • Conventional commit message, branch fix/<description>
  • PR based on main (default branch)
  • No behavior change for tables without filter columns (thead persistence is transparent, events delegated)

…oses focus (rant 2026-08-25T11:15:16)
Column-filter inputs on the transactions page lost focus after every
keystroke (regression of PR #146 server-side filters, v0.7.18): the
debounced filter change triggered an async fetch, and the fetch-return
render rebuilt the whole table via innerHTML, destroying the focused
input. Focus-restore ran after the wrong render.
Fix (host-confirmed option B): the header filter row is now rendered
once and survives rebuilds — buildDataTable only rebuilds tbody and the
pager; the input DOM is never destroyed so focus is naturally preserved.
- split thead (sort buttons + filter row) into tableTheadHtml, rendered
only when the container has no <table> yet (values seeded from
state.filters; afterwards the inputs are the DOM source of truth)
- tbody (tableBodyHtml) and pager rebuilt on every render as before
- per-render event binding replaced with one-time container-level
delegation (sort / filter input / pager / page-size / IME), reading
the latest config via container._dt so events survive rebuilds
- IME composition tracked (compositionstart/end + focusout safety):
no table rebuild / no setSelectionRange while composing, refresh
debounced once after compositionend — Chinese IME input is not
interrupted
- focus restore only as fallback when the input is not focused
- cache-bust 20260825-2
@argszero
argszero merged commit 4ee2a2c into mainAug 25, 2026
1 check passed
@argszero
argszero deleted the fix/tx-filter-focus branch August 25, 2026 03:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@argszero