WICG / WICG/declarative-partial-updates

Clarify semantics and author guidance for user-modified form state (value, focus, selection) when a patch replaces the containing range

Open
#103 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Bikeshed
Stars
135
Forks
13
Avg merge
12m
Merged PRs (30d)
3

Description

Summary

When a <template for> patch replaces a range that contains a form control the user has modified — including the currently focused <input> — the range contents are removed and replaced, per the patching explainer. Observed in Chromium with chrome://flags/#enable-experimental-web-platform-features enabled:

  1. The user-entered value is lost; the new input shows the server-provided value.
  2. If the control was focused, focus moves to <body>, consistent with focus fixup when the focused node is removed.
  3. Selection/caret position and IME composition state are presumably lost as well, since the original node is replaced (I have not tested composition directly).

This appears consistent with the explainer's replacement semantics, so I am not reporting it as an implementation bug. I'd like to raise it as a spec/documentation question with real-world form UX implications.

Relationship to existing discussions

  • #39 raised state loss on patch replacement in general terms (e.g. <details> open state, chat widgets re-initializing across navigations).
  • #41 (closed) led to the "avoiding overwriting with identical content" / contentrevision enhancement in the explainer. That mechanism skips a patch whose content is identical to what's already there. It doesn't cover this case: here the patch content has legitimately changed (new results, new validation message), and the state at risk is user-modified form state inside the range, not the streamed content itself.
  • #32 (auto-submitting forms) would make patches racing with active typing a common, platform-encouraged pattern. That issue is about triggering patches from form changes; this one is about what happens to the active control when the resulting patch lands.

Why this matters

The canonical use cases for streamed patches — live search, faceted filtering, validation feedback — involve the server patching regions while the user is typing. If the patched range includes the input (a natural way for a server-rendered component to express "the field plus its validation message" or "the search box plus its results"), each streamed patch silently reverts the user's in-progress input and drops focus.

Libraries that update live HTML trees (React reconciliation, morphdom/idiomorph, Turbo morphing) all ended up special-casing focused/dirty form controls for exactly this reason.

Minimal repro

Repro repository: https://github.com/federicobartoli/dpu-focus-preservation-repro

Three cases:

  1. Control: the focused input is outside the patched range; a sibling range is patched. Value and focus survive.
  2. Focused, inside range: the focused input is inside the patched range. The user-entered value is replaced by the server value and focus moves to <body>.
  3. Dirty but unfocused, inside range: an input with a user-modified value, not focused, is inside the patched range. The modified value is lost. This shows the value loss is a property of range replacement itself; the focus loss is an additional effect.

Reduced case 2:

<div id="focus-host">
  <?start name="profile-form">
  <label>
    Name
    <input id="name" name="name" value="" autocomplete="off">
  </label>
  <p>Fallback form is editable before validation arrives.</p>
  <?end>
</div>

<!-- streamed later -->
<template for="profile-form">
  <label>
    Name
    <input id="name" name="name" value="server-value" autocomplete="off">
  </label>
  <p>Server validation arrived.</p>
</template>

Observed result (case 2):

{
  "destructiveBeforeContainingRangePatch": {
    "activeElement": "input#name",
    "destructiveValue": "typed-before-containing-range-patch"
  },
  "destructiveAfterContainingRangePatch": {
    "activeElement": "body#",
    "destructiveValue": "server-value"
  }
}

Observed result (case 3):

{
  "dirtyUnfocusedBeforePatch": {
    "activeElement": "body#",
    "notesValue": "typed-earlier-not-focused"
  },
  "dirtyUnfocusedAfterPatch": {
    "activeElement": "body#",
    "notesValue": "server-notes"
  }
}

Environment: Chrome 149.0.7827.201, experimental web platform features enabled.

Questions

  1. Is "replacement discards user-modified state inside the patched range" the intended final semantic, with the guidance being that authors should keep actively edited controls outside patched ranges (e.g. via marker placement)?
  2. If so, could the explainer call this out explicitly, ideally with a marker-placement example for form validation / live search?
  3. Has an opt-in preservation mechanism been considered — in the spirit of contentrevision, but keyed on user-modified form state rather than content identity? Or is this intentionally left to higher-level frameworks?
  4. Should eventual spec text define focus/selection behavior for this case normatively, so other engines converge?

To restate: I'm not assuming this is an implementation bug. The point is developer expectation — if Declarative Partial Updates are used for real server-rendered UI, active form state is one of the first sharp edges authors will hit.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the patching explainer and the minimal reproduction linked in the issue, then compare the behavior with discussions #39, #41, and #32. Done means deciding the intended treatment of user-modified form state, focus, selection, and composition, and documenting author guidance or normative requirements if maintainers agree.

Written by the indexing model from the issue text.

Assessment

Tech stack
html
Domain
documentation, web-dev
Issue type
Documentation
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.