fix(ui): keep filter row alive during table rebuild so typing never loses focus (rant 2026-08-25T11:15:16) - #148
Merged
Merged
Conversation
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 →
renderTransactionsdetects the filter-signature change →loadTransactions()async fetch → after fetch returns,renderTransactions→buildDataTableexecutescontainer.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 —
buildDataTablenow 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):Changes
ui/js/app.js:tableTheadHtml()— thead (sort buttons + filter row) rendered once, only when the container has no<table>yet; input values seeded fromstate.filterson first render, afterwards the inputs are the DOM source of truthtableBodyHtml()— data rows, rebuilt on every renderbuildDataTable()— rebuilds only tbody + pager; container-level one-time event delegation (sort / filter input / pager / page-size / IME) reads the latest config viacontainer._dt, so events survive rebuilds and are never lost after the first keystrokecompositionstart/compositionend+focusoutsafety): no table rebuild and nosetSelectionRangewhile composing; a refresh is debounced aftercompositionend— Chinese IME composition is not interruptedui/index.html: cache-bust bumped to20260825-2Tests
node --check ui/js/app.js— OKcargo test— 147/147 passChecklist
fix/<description>main(default branch)