Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creating non-ugraded defined custom element : createElement(tagname, {upgrade:false}) #1097

Open
denis-migdal opened this issue Feb 17, 2025 · 9 comments

Comments

@denis-migdal
Copy link

It could be great if we could easily create non-ugraded defined custom element.

Currently, to do that, we need to create a <template> element, set its content with .innerHTML, and then fetch the element :

const t = document.createElement("template");
t.innerHTML = "<my-tag></my-tag>";
const elem = t.content.firstChild;

We can also create the element on another document, but then the element is impossible to upgrade :

const doc = new Document();
const elem = doc.createElement("my-tag");
@annevk
Copy link
Collaborator

annevk commented Feb 17, 2025

I suspect this will be the result with document.createElement(..., { customElements: null }), but I've yet to actually define that as part of whatwg/dom#1339.

cc @rniwa

@annevk
Copy link
Collaborator

annevk commented Feb 17, 2025

Hmm, why would it be impossible to upgrade in the latter case?

@denis-migdal
Copy link
Author

Hmm, why would it be impossible to upgrade in the latter case?

Maybe this is an implementation issue, but on Firefox and Chromium, customElements.upgrade() does nothing on an element created on another document. even after document.adoptNode(elem) and/or document.body.append(elem).

class A extends HTMLElement {}

customElements.define("a-a", A);

const d = new Document();
const a = d.createElement("a-a");

document.adoptNode(a)
document.body.append(a);
customElements.upgrade(a)

console.log(a instanceof A); // gives false

@annevk
Copy link
Collaborator

annevk commented Feb 18, 2025

That's because you're creating an element in the null namespace rather than the HTML namespace. Use const a = d.createElementNS("http://www.w3.org/1999/xhtml", "a-a"); and it'll work.

@denis-migdal
Copy link
Author

Oh, thanks, I didn't know.

It seems we can even do:

const doc = document.implementation.createDocument(
  "http://www.w3.org/1999/xhtml",
  "html",
  null,
);
// document.implementation.createHTMLDocument() also possible, but will create .body properties, etc.

doc.createElement("a-a");

@denis-migdal
Copy link
Author

denis-migdal commented Feb 18, 2025

I just noticed that in the two solutions (template and HTML namespace) :

  1. the created HTMLElement isn't an HTMLUnknownElement (seems also to be the case when built with .innerHTML) ;
  2. customElement.upgrade() has no effect on them if we didn't adopt or insert them in the main document.

I think the (1) behavior is an issue as we might have some code using instanceof HTMLUnknownElement to detect unupgraded custom elements. Fortunately, it seems that :not(:defined) works.

So we need to do something like:

const doc = document.implementation.createDocument(
  "http://www.w3.org/1999/xhtml",
  "html",
  null,
);

function createElement(tagname) {
      const elem = doc.createElement(tagname);
      document.adoptNode(elem);
      return elem;
}

@annevk
Copy link
Collaborator

annevk commented Feb 18, 2025

I don't think HTMLUnknownElement is ever used for something that has a valid custom element name (and is in the correct namespace)?

That upgrade() doesn't work without the correct node document follows from how upgrade() is defined today. It's actually a bit surprising that it doesn't use the registry upon which upgrade() is invoked but instead tries to find it through the node document. This will work with scoped custom element registries.

@denis-migdal
Copy link
Author

I don't think HTMLUnknownElement is ever used for something that has a valid custom element name (and is in the correct namespace)?

Indeed, I may have misread the MDN documentation.

It seems the best way would then to use elem.constructor.name === "HTMLElement" to check if an element has been upgraded.

@annevk
Copy link
Collaborator

annevk commented Feb 18, 2025

I would recommend always checking with the specification. (Filing an issue against MDN if you find that page where it claims that would be useful.) If you search for HTMLUnknownElement on https://html.spec.whatwg.org/ it's pretty clear.

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

No branches or pull requests

2 participants