Skip to content

Attribute writes on a disconnected element are silently dropped #103

@DmitrySharabin

Description

@DmitrySharabin

Repro

let el = document.createElement("my-element");
el.setAttribute("foo", "bar");      // attribute set while disconnected
document.body.append(el);
console.log(el.foo);                // expected: "bar", actual: undefined

Cause

Props#attributeChanged early-returns when !element.isConnected:

attributeChanged (element, name, oldValue) {
    if (!element.isConnected || element.ignoredAttributes.has(name)) {
        // We process attributes all at once when the element is connected
        return;
    }
    ...
}

The comment promises "we process attributes all at once when the element is connected" — but no such re-scan exists on the connected lifecycle. Props#connected only drains the dispatch queue; it doesn't walk observedAttributes. So the attribute write is observed by the browser (via attributeChangedCallback), routed to Props#attributeChanged, bailed on, and never replayed.

The parser-upgrade path works because isConnected is true during constructor (the element is already in the document by the time customElements.define upgrades it). Only the createElement → setAttribute → append path is affected.

Possible fixes

  1. Drop the !isConnected guard. Let attributeChanged proceed even when disconnected; the queue + connected() drain already handle deferred dispatch.
  2. Scan observedAttributes in Props#connected. Mirror what initializeFor does, but only for attributes whose corresponding prop hasn't been set yet.

(1) is simpler and likely correct given the post-PR-#102 architecture (queue + drain handles disconnected elements).

Pre-existing

This bug exists on main and was preserved (not introduced) by #102. Surfaced during code review of that PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions