Skip to content

Add native <form> submission fallback - #93

Merged
tannevaled merged 1 commit into
mainfrom
feat/form-submit
Sep 2, 2026
Merged

tannevaled merged 1 commit into
mainfrom
feat/form-submit

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

F4 of the interactive-form-input plan (#89#91): the path for a plain server-rendered login form with no JS submit handler at all.

LiveDocument.Submit(ctx, form) dispatches submit through the live session first, exactly like a real browser. If a listener calls preventDefault() (the common SPA case, already reachable via Focus/Type/Click plus the engine's existing fetch()/XHR support), nothing further happens and the current page stays open. Otherwise it gathers named form-control values and performs the real GET-with-querystring or POST-urlencoded request per the form's action/method, opens the response as a new LiveDocument, and closes the old one — a real navigation ending the previous page's JS, same as a browser.

Two real gaps surfaced while writing this against the actual DOM model: <select> needed its own selected-option walk (nothing at the engine-package level did this yet), and <textarea> has no dedicated JS value accessor at all (js/dom.go's generic value property just reads the value attribute, which a textarea never carries until something sets it) — Submit's gathering falls back to the element's text content for an untouched textarea, matching real textarea.value semantics, with the JS-binding gap documented for whoever hits it from script.

Test plan

  • Full suite + -race pass, go vet/gofmt clean
  • 100% coverage on every function except postForm (83.3% — remaining gaps are low-level I/O failure branches, matching this codebase's own existing tolerance: Fetch/decodeCharset sit at 82.4%/75% on main for the same class of near-unreachable path)
  • TestSubmitPOST/TestSubmitGETAppendsQuery — real HTTP round-trip against an httptest server, checking the server actually received the typed values
  • TestSubmitSkippedWhenJSPreventsDefault — no request made when a script intercepts submit
  • TestFormDataGathering — checkbox/radio/disabled/select/textarea/button-exclusion all in one fixture

🤖 Generated with Claude Code

F4 of the interactive-form-input plan (#89-#91): the path for a plain
server-rendered login form with no JS submit handler at all.

LiveDocument.Submit(ctx, form) dispatches "submit" through the live
session first, exactly like a real browser. If a listener calls
preventDefault() (the common SPA case, already reachable via
Focus/Type/Click plus the engine's existing fetch()/XHR support),
nothing further happens and the current page stays open. Otherwise it
gathers named form-control values (input/textarea/select, skipping
disabled controls and unchecked checkboxes/radios, matching what a
real browser submits) and performs the actual GET-with-querystring or
POST-urlencoded request per the form's action/method, then opens the
response as a new LiveDocument and closes the old one — a real
navigation ending the previous page's JS, same as a browser.

Caught two real gaps while writing this against the actual DOM model
rather than assuming: <select>'s submitted value needs its own
selected-option walk (no shared helper existed at the engine-package
level); <textarea> has no dedicated JS value accessor (js/dom.go's
generic "value" property just reads the "value" attribute, which a
textarea doesn't carry until something explicitly sets it) — Submit's
own value-gathering falls back to the element's text content for an
untouched textarea, matching what a real browser's textarea.value
defaults to, while documenting the JS-binding gap for anyone who hits
it from script instead.

100% coverage on every function except postForm (83.3% — the
remaining gaps are low-level I/O failure branches http.NewRequest/
ReadAll/decodeCharset/dom.Parse can each return, consistent with this
codebase's own existing tolerance for that class of near-unreachable
path: Fetch and decodeCharset sit at 82.4%/75% on main today for the
same reason). Full existing suite + -race unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit d90f8a7 into main Sep 2, 2026
7 checks passed
@tannevaled
tannevaled deleted the feat/form-submit branch September 2, 2026 18:51
tannevaled added a commit that referenced this pull request Sep 3, 2026
tannevaled added a commit that referenced this pull request Sep 3, 2026
* fix(css): hide <option> content instead of leaking it into layout

A <select> falls into the generic display:inline UA bucket alongside
<button>/<input>, but unlike those its <option> children carry real
visible text with no UA rule to say a native control never lays it
out. Observed live on pkg.go.dev/net/http: a version/tab-switcher
<select> holding the page's entire symbol index as option text
rendered as a garbled, concatenated block near the top of the page.

Fixed with the same precedent already used for <template>'s inert
content: option { display: none }. Matches the existing, accepted
simplification that an <input>'s value is never shown either.

pkg.go.dev/net/http: SSIM 0.530->0.616, pixdiff 45.8%->40.0%. Full
10-page bench corpus re-run confirms nothing else regressed.

* fix PR number citation: engine#93 -> engine#94 (a concurrent PR landed as #93 while this branch was in flight)
Sign up for free to 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