Manual focus handling versus Backbone.Accessibility mixin for focus restoration in nested Backbone.js views
0 reputation · 06 Jul 2022, 00:11 UTC
0 reputation · 06 Jul 2022, 00:11 UTC
The goal is to ensure that keyboard focus returns to the element that had focus before a Backbone.js view is removed and replaced, especially when views are nested within each other.
Manual focus handling requires developers to store document.activeElement in the view’s remove method and restore it in render, which adds boilerplate and can be missed in error paths or asynchronous callbacks. The Backbone.Accessibility mixin provides an automatic focusManager attribute but introduces an external dependency whose maintenance status varies across forks, raising uncertainty about long‑term compatibility.
34275 reputation · 06 Jul 2022, 06:44 UTC
Neither approach is inherently more reliable in deeply nested hierarchies — both fail the same way if any view in the chain skips the save/restore step. The mixin's real advantage is consistency: it forces the focus bookkeeping into every view uniformly, which is exactly where manual handling breaks down. If you can verify that your fork of Backbone.Accessibility implements focusManager correctly against your Backbone version, use the mixin. If you can't verify it, a small hand-rolled mixin of your own is safer than scattering ad-hoc document.activeElement snapshots across views.
When a focused element is removed from the DOM, the browser moves focus to document.body. In a nested hierarchy, the parent view's remove() typically tears down child views too, so the focused element is destroyed before any child-level restore logic runs. Restoring focus in the child's render() then fails silently because the saved element reference points to a detached node. The fix in both approaches is the same: save the selector or a stable identifier (not just the node reference) before removal, and after re-render, re-query the DOM and focus the matching element if it still exists.
With CSS transitions or animation callbacks, the timing rule is: capture focus synchronously at the start of removal (before the transition begins), and restore only after the new view is fully inserted — i.e., in the transition's completion callback or a promise .then(), not in render() itself if render happens before re-insertion. Manual code often gets this wrong by restoring too early. A well-implemented focusManager hooks the view's removal lifecycle and defers the restore, but you should confirm your fork actually does this rather than assuming it.
Build a minimal test: parent view containing a child with an input. Focus the input, remove the parent with a transition delay, re-render, and assert document.activeElement is the input again. Run it with your manual code and with the mixin applied to both levels. One detail that changes the recommendation: which Backbone.Accessibility fork and Backbone version are you on? If the fork's focusManager hasn't been updated for your version, a 20-line custom mixin is the lower-risk path.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.