Skip to content

Passing the scoped custom element registry to the fragment parser has non-obvious side effects #12274

Description

@noamr

What is the issue with the HTML Standard?

Currently, the fragment parser uses the custom element registry of its context element when parsing.
(https://html.spec.whatwg.org/multipage/parsing.html#html-fragment-parsing-algorithm step 8)

This has some observable side effects that might not be ideal.
Specifically, the custom element is upgraded and its constructor is invoked at fragment-parse time rather than at connection time.

It is observable in the following scenarios:

  • setting innerHTML on a disconnected element with a registry upgrades its custom elements and calls the constructors
  • When creating a contextual fragment, the elements are upgraded based on the context but the fragment might be added to a different element in the end
  • If there is any change in the registry between parsing time and connection time, the first one wins.
  • More acutely - see https://github.com/WICG/sanitizer-api/issues/381. Elements are upgraded before the sanitizer gets to remove them.

Not sure how to go about this, since it's the only way those element-scoped registries are respected.

Activity

  1. added
    topic: custom elementsRelates to custom elements (as defined in DOM and HTML)
    agenda+To be discussed at a triage meeting
    on Mar 17, 2026
  2. noamr commented on Mar 17, 2026

    @noamr
    ContributorAuthor

    Also in general the behavior of an element's scoped registry (without shadow) is really strange IMO...

    e.g., the following wouldn't upgrade the element:

    const customElementRegistry = new CustomElementRegistry()
    customElementRegistry.define("x-element", 
      class MyElement extends HTMLElement { 
        constructor() { super(); console.log("ctor"); }  
      }
    );
    
    const doc = new DOMParser().parseFromString("<x-element></x-element>", "text/html")
    
    const container = document.createElement("div", {customElementRegistry});
    document.body.append(container);
    
    // The element is adopted and connected, but wouldn't upgrade to anything.
    container.append(doc.querySelector("x-element"))

    But if you add container.innerHTML = container.innerHTML at the end, it would, because then the "fragment parser takes custom element registry into account" kicks in.

    This feels non-obvious and unintuitive to me.

  3. keithamus commented on Mar 18, 2026

    @keithamus
    Member

    This is a little related: #12209

  4. added
    agenda+To be discussed at a triage meeting
    and removed
    agenda+To be discussed at a triage meeting
    on Mar 18, 2026
  5. sorvell commented on Mar 18, 2026

    @sorvell

    This has some observable side effects that might not be ideal.

    I think everything noted here except the interaction with the sanitizer is by design and expected. The registry is determined at construction time and cannot mutate after that, unless it starts as null.

    Also in general the behavior of an element's scoped registry (without shadow) is really strange IMO...

    The behavior specifically related to adopting elements from different documents is treated specially for backwards compatibility. This means that the behavior of parseFromString and innerHTML is different because in the former case the elements are created in a different document with a null registry. See this example evolved from the parseFromString code above.

  6. removed
    agenda+To be discussed at a triage meeting
    on Mar 26, 2026
  7. keithamus commented on Jun 1, 2026

    @keithamus
    Member

    Looking over the past notes I am not sure this was discussed, at least I couldn't find a resolution, but we should resolve this. I think deserializing with a null reg and initializing post-serialisation makes the most sense. But I'd like us to decide on that before making the necessary changes.

  8. removed
    agenda+To be discussed at a triage meeting
    on Jun 23, 2026
  9. annevk commented on Aug 24, 2026

    @annevk
    Member

    @noamr if this is fixed by your sanitizer streaming PR as the minutes imply, can you couple it to your PR so this will get closed when we land that change?

  10. noamr commented on Aug 24, 2026

    @noamr
    ContributorAuthor

    @noamr if this is fixed by your sanitizer streaming PR as the minutes imply, can you couple it to your PR so this will get closed when we land that change?

    I think we can close this as #12549 already tracks the sanitizer issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: custom elementsRelates to custom elements (as defined in DOM and HTML)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions