What happens
On the form prototypes, selecting an answer on a radio question scrolls the page back to the top. You lose your place and have to scroll down again to continue the step.
Example — Prototypes/nhc-rental-application.html:
Do you or your spouse own land or property?
Steps to reproduce
- Open
Prototypes/nhc-rental-application.html.
- Go to the step containing "Do you or your spouse own land or property?" and scroll so the question is below the fold.
- Select Yes or No.
- The page jumps to the top.
Expected: the scroll position stays put and focus stays on the option you just chose; only the revealed/hidden conditional content changes.
Actual: the whole step is re-rendered, the page scrolls to the top, and focus on the radio is lost.
Cause
It's the shared framework, not the individual prototype.
Radios marked { trigger: true } get a data-trigger-render="1" attribute. In assets/govbb-framework.js the change handler calls a full re-render:
// assets/govbb-framework.js:201
if (el.getAttribute('data-trigger-render')) GovBB.render();
And GovBB.render() rebuilds the step and then unconditionally scrolls to the top:
// assets/govbb-framework.js:47
GovBB.render = function () {
...
el.innerHTML = fn(); // whole step rebuilt — focus is destroyed
...
window.scrollTo(0, 0); // line 63 — always fires
};
window.scrollTo(0, 0) is the right behaviour when render() is called for step navigation (GovBB.next / GovBB.prev, lines 130–150), but wrong when it's called for a mid-step conditional reveal. Both paths share the same function, so the conditional reveal inherits the scroll reset.
Suggested fix
Give render() a way to distinguish the two callers — e.g. GovBB.render({ scroll: false }), with the data-trigger-render handler passing scroll: false and the navigation callers keeping the current behaviour. Restoring focus to the field that triggered the re-render would also be needed, since innerHTML = fn() throws focus away.
Scope — wider than this one form
data-trigger-render is used across many prototypes, so this affects all of them. Grep shows it on radios, selects and text inputs in at least: nhc-rental-application, nhc-state-land, terms-leave, uniform-grant, textbook-grant, home-schooling, special-ed-bursary, direct-deposit, bssee-choice, bssee-defer, exam-fee-refund, examination-travel, exam-duties-claim, nscp-camper-registration, temporary-teacher, non-nationals-entry.
Worth checking as part of the same fix: for text inputs the bound event is input rather than change (govbb-framework.js:196), so a text field carrying data-trigger-render — e.g. child-school in uniform-grant.html — would re-render, scroll to top and drop focus on every keystroke. I've read this from the code but not run it, so it needs confirming in the browser.
Why it matters
An unexpected change of scroll position and loss of focus on input is what WCAG 2.2 3.2.2 On Input is about. For keyboard and screen reader users the focus loss is the more serious half — they're returned to the top of the document mid-question.
What happens
On the form prototypes, selecting an answer on a radio question scrolls the page back to the top. You lose your place and have to scroll down again to continue the step.
Example —
Prototypes/nhc-rental-application.html:Steps to reproduce
Prototypes/nhc-rental-application.html.Expected: the scroll position stays put and focus stays on the option you just chose; only the revealed/hidden conditional content changes.
Actual: the whole step is re-rendered, the page scrolls to the top, and focus on the radio is lost.
Cause
It's the shared framework, not the individual prototype.
Radios marked
{ trigger: true }get adata-trigger-render="1"attribute. Inassets/govbb-framework.jsthe change handler calls a full re-render:And
GovBB.render()rebuilds the step and then unconditionally scrolls to the top:window.scrollTo(0, 0)is the right behaviour whenrender()is called for step navigation (GovBB.next/GovBB.prev, lines 130–150), but wrong when it's called for a mid-step conditional reveal. Both paths share the same function, so the conditional reveal inherits the scroll reset.Suggested fix
Give
render()a way to distinguish the two callers — e.g.GovBB.render({ scroll: false }), with thedata-trigger-renderhandler passingscroll: falseand the navigation callers keeping the current behaviour. Restoring focus to the field that triggered the re-render would also be needed, sinceinnerHTML = fn()throws focus away.Scope — wider than this one form
data-trigger-renderis used across many prototypes, so this affects all of them. Grep shows it on radios, selects and text inputs in at least:nhc-rental-application,nhc-state-land,terms-leave,uniform-grant,textbook-grant,home-schooling,special-ed-bursary,direct-deposit,bssee-choice,bssee-defer,exam-fee-refund,examination-travel,exam-duties-claim,nscp-camper-registration,temporary-teacher,non-nationals-entry.Worth checking as part of the same fix: for text inputs the bound event is
inputrather thanchange(govbb-framework.js:196), so a text field carryingdata-trigger-render— e.g.child-schoolinuniform-grant.html— would re-render, scroll to top and drop focus on every keystroke. I've read this from the code but not run it, so it needs confirming in the browser.Why it matters
An unexpected change of scroll position and loss of focus on input is what WCAG 2.2 3.2.2 On Input is about. For keyboard and screen reader users the focus loss is the more serious half — they're returned to the top of the document mid-question.