Skip to content

Conversation

@foolip
Copy link
Member

@foolip foolip commented Jan 22, 2026

This node is intended for the <!start>, <!end> and <!marker>
syntax, to be created by the HTML parser.

  • At least two implementers are interested (and none opposed):
  • Tests are written and can be reviewed and commented upon at:
  • Implementation bugs are filed:
    • Chromium: …
    • Gecko: …
    • WebKit: …
    • Deno (only for aborting and events): …
    • Node.js (only for aborting and events): …
  • MDN issue is filed: …
  • The top of this comment includes a clear commit message to use.

(See WHATWG Working Mode: Changes for more details.)

This node is intended for the `<!start>`, `<!end>` and `<!marker>`
syntax, to be created by the HTML parser.
@foolip foolip marked this pull request as draft January 22, 2026 15:01
const unsigned short DOCUMENT_TYPE_NODE = 10;
const unsigned short DOCUMENT_FRAGMENT_NODE = 11;
const unsigned short NOTATION_NODE = 12; // legacy
const unsigned short MARKER_NODE = 13;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love it, happy to provide a polyfill around this too, in case nobody else is already working on it

Copy link

@WebReflection WebReflection Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

example:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script type="module">
    class Marker extends CharacterData {
      get nodeType() {
        return 13;
      }
      hasAttribute(name) {
        return new RegExp(` ${name}(=(['"])?(.*)?\\2)?`).test(this.data);
      }
      getAttribute(name) {
        return this.hasAttribute(name) ? RegExp.$3 : null;
      }
    }

    const promoted = new WeakSet;
    const tw = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT);
    let node;
    while (node = tw.nextNode()) {
      if (!promoted.has(node) && /^(start|end|marker)(?:$|\s+)/.test(node.data)) {
        promoted.add(node);
        const kind = RegExp.$1;
        Object.setPrototypeOf(node, Marker.prototype);
        console.log({ name: node.getAttribute('name') });
      }
    }
  </script>
</head>
<body>
  <!start>
  O
  <!marker name=unquoted>
  <!marker name="double quoted">
  <!marker name='single quoted'>
  <!marker-nope name="value">
  <!nope name="value">
  K
  <!end>
</body>
</html>

now, I've no idea how people out there are supposed to distinguish <!marker> from <!--marker-->, as example, but the polyfill would take those starting content for granted and produce a Marker node already.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forgot the devtools result:

image

@foolip
Copy link
Member Author

foolip commented Jan 22, 2026

@annevk this isn't complete, but I wanted to send it out because I'm not totally sure about a few choices.

  • I think a single interface with a read-only type (marker/end/start) makes sense, but unlike elements you can't create a node with any string. I don't think we can allow new Marker("doctype", "html"), but am not sure if DOM should allow new Marker("whatever") even where HTML wouldn't parse <!whatever> into such a node.
  • Should we have document.createMarker() in addition to the constructor?

@annevk
Copy link
Member

annevk commented Jan 22, 2026

We probably need to investigate why ShadowRoot didn't get a new node type. I think a constructor probably suffices, but we should restrict it to match what HTML can do.

@WebReflection
Copy link

WebReflection commented Jan 22, 2026

document.createMarker(kind = 'marker', attributes = {}) would be a great API to me, it creates a nodeType 13 and it will throw if kind is not marker, start or end + it will add "escaped marker attributes" out of the box, with quoting and everything.

@foolip
Copy link
Member Author

foolip commented Jan 22, 2026

We probably need to investigate why ShadowRoot didn't get a new node type.

I wondered about this too, and guess it's because it inherits from DocumentFragment which made it possible to use DOCUMENT_FRAGMENT_NODE.

ShadowRoot was added in b2ee400, but I don't see any clues from that.

Is there some place you suspect the discussion might have happened that I can go investigate?

@annevk
Copy link
Member

annevk commented Jan 22, 2026

Maybe in WebApps archives? I think there was also some aversion to introducing a new node type and having to revisit existing algorithms. But I'm not sure that ended up being good thing as we still have some technical debt to resolve from that.

@foolip
Copy link
Member Author

foolip commented Jan 22, 2026

I found #1007 via list archives, but it's not a discussion on whether constant should be added or not.

From a search of Chromium commit messages and WebKit bugs, I found some decisions:

I haven't looked at the spec history, but from this I gather the reasoning was that the new constant should only be added if the new interface inherits directly from Node, as it cannot be avoided in that case. Since we're suggesting that Marker inherit from Node, it will need a new type and we'll need to consider it in places like NodeIterator and TreeWalker as well.

We will also need a new "#marker" return value for node.nodeName, and probably a bunch of other little changes.

@foolip
Copy link
Member Author

foolip commented Jan 22, 2026

The original spec edit to use DOCUMENT_FRAGMENT_NODE for ShadowRoot seems to be WICG/webcomponents@0700c45, and I also found it discussed in WICG/webcomponents#324, with nobody arguing for a different type than document fragments, so it was left that way.

@WebReflection
Copy link

to whom it might concern, I've read the proposed specs and created a polyfill:
https://gist.github.com/WebReflection/291357aa6bbbd97d54a971ce5f5aae4e

I took a chance to add attributes but it's unclear in this document how those would be handled.

@WebReflection
Copy link

WebReflection commented Jan 23, 2026

We will also need a new "#marker" return value for node.nodeName, and probably a bunch of other little changes.

I missed that, updated the gist to reflect that too ... it's looking good to me as polyfill:

  • the class extends Node, not comment
  • both type and name are reflected
  • all nodes are updated in the living DOM and in each ShadowRoot
  • literally nothing else happens

You can new Marker(type, name = '') out of the box but comments found live or appended will be automatically promoted, no "node substitution" needed, just like custom elements would.

I hope this will help shaping the specs.

edit P.S. as every polyfill in this world kinda requires, that module should be the very first one imported on top of everything else, yet it will play nicely with other polyfills, as it shouldn't bother any other related logic.

readonly attribute MarkerType type;
readonly attribute DOMString name;
};
</pre>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also include mixin NonDocumentTypeChildNode I think.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

5 participants