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

registry: Add an API to retrieve the localName given a custom element constructor #566

Open
treshugart opened this issue Sep 14, 2016 · 56 comments

Comments

@treshugart
Copy link

I've read the spec and I can't find any mention of how to get the localName of an element from the constructor. In v0 (I know, don't rely on old Blink) you could:

const Ctor = document.registerElement('x-test', {
  prototype: Object.create(HTMLElement.prototype)
});

// x-test
console.log(Ctor.name);

In Chrome Canary this behaves as I'd expect:

class Ctor extends HTMLElement {}
window.customElements.define('x-test', Ctor);

// Ctor
console.log(Ctor.name);

I can see why this is but I feel there's use cases for this. For example, when exporting a custom element constructor, consumers may need to know the name that it got registered with. In https://github.com/webcomponents/react-integration, we use the registered name in order to tell React the element it should create in the virtual DOM. Currently we have to construct the component and get the tagName from it.

A proposal for this may be to add a method to CustomElementRegistry which retrieves the name of the element when passed a constructor:

// any getLocalName(Function constructor)
window.customElements.getLocalName(Ctor);

It could return null if not found, or the localName if found.

Thoughts?

@domenic
Copy link
Collaborator

domenic commented Sep 14, 2016

There is no way to do this. As you say, constructing the component and retrieving the .localName from it is the best way currently.

It sounds like the one use case you've provided so far is working around the fact that React cannot use normal element constructors, i.e. it insists on using createElement? Are there any other use cases that aren't working around framework-specific limitations?

@treshugart
Copy link
Author

treshugart commented Sep 15, 2016

Yeah, React needs the local name so it can do it within the patching algorithm.

In Skate we use Incremental DOM and though it leverages the actual DOM, we still have to tell it the localName of the element to create. This information we currently store internally and pass on.

In the same library (Skate), we also offer a way to auto-generate unique tag names. This is useful if you have two versions of the same component arriving on the same page, for whatever reason. It could be someone is slowly upgrading to the new version of your component, Webpack HMR, writing tests without ensuring the component name is unique (because you can't unregister in a teardown step), etc. While this is trivial to do at the library level - and should be done at the library level - it'd be nice for integrations if there was a standard way to retrieve the localName the constructor was registered for, that way component consumer's don't have to rely on the library the component was written in to do so. To them it's just a web component, as opposed to a Skate component.

Piggy-backing off that, a more compelling use-case may be for libraries like iDOM or React to be able to check internally that if a function is passed (React already supports this as stateless functions), to see if it's a custom element constructor (maybe Func.prototype instanceof HTMLElement or something). If so, then it could do customElements.getLocalName(Func) and then do the diffing and patching. Patching could be done using the constructor if a new element is needed, however, you probably wouldn't want to create a new element to get the localName just to diff element types. Furthermore, instanceof probably wouldn't be appropriate here because Func could be a subclass of the element being compared (and this is assuming they even use real DOM nodes - iDOM does, but I don't think React and other implementations do).

There's probably other use-cases, but those are definitely ones close to our domain that we've encountered and some we use on a daily basis.

@treshugart
Copy link
Author

I also haven't considered customised built-ins here and haven't thought about it much yet.

@rniwa
Copy link
Collaborator

rniwa commented Sep 16, 2016

We could add something like getName on document.customElements that gives you the name of the custom element given the constructor, which you can obtain via new.target.

@treshugart
Copy link
Author

That'd do the trick!

@rniwa
Copy link
Collaborator

rniwa commented Sep 16, 2016

My proposal would be renaming the existing get to getInterface and add getName to make the semantics clear. We could even call them findInterface and findName as well.

@domenic
Copy link
Collaborator

domenic commented Sep 16, 2016

The use cases still aren't clear to me; they seem very speculative, relating to things that maybe future versions of some libraries might choose to do, or to things that can already be done at the library level.

@treshugart
Copy link
Author

I don't agree that all my use cases were speculative. If we don't have a
standardised way to retrieve a tag name for a constructor, it forces us to
store that information locally. Since part of our API is to have multiple
working versions on the page, we then force internal APIs to never change
else we break that contract. This would be a really nice feature for us to
have.

On Sat, 17 Sep 2016, 02:33 Domenic Denicola notifications@github.com
wrote:

The use cases still aren't clear to me; they seem very speculative,
relating to things that maybe future versions of some libraries might
choose to do, or to things that can already be done at the library level.


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
#566 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAIVbHhRY2qLU-gMgCDlXEdk3gdRt_yeks5qqsTNgaJpZM4J8SND
.

@treshugart
Copy link
Author

And FWIW I second @rniwa's proposal, though get* might follow existing
conventions more closely

On Sat, 17 Sep 2016, 08:59 Trey Shugart treshugart@gmail.com wrote:

I don't agree that all my use cases were speculative. If we don't have a
standardised way to retrieve a tag name for a constructor, it forces us to
store that information locally. Since part of our API is to have multiple
working versions on the page, we then force internal APIs to never change
else we break that contract. This would be a really nice feature for us to
have.

On Sat, 17 Sep 2016, 02:33 Domenic Denicola notifications@github.com
wrote:

The use cases still aren't clear to me; they seem very speculative,
relating to things that maybe future versions of some libraries might
choose to do, or to things that can already be done at the library level.


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
#566 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAIVbHhRY2qLU-gMgCDlXEdk3gdRt_yeks5qqsTNgaJpZM4J8SND
.

@bradleyayers
Copy link

bradleyayers commented Sep 19, 2016

I echo @domenic's sentiment. I had a chat with @treshugart offline and suggested changing Incremental DOM to allow it to be agnostic to the element name when given a constructor.

(It seems like it should be able to do a elem.constructor === Ctor check when diffing the DOM, rather than relying on some elem.tagName === getName(Ctor) logic.

@treshugart
Copy link
Author

Yeah, bouncing ideas off @bradleyayers helped. Though, maybe we're missing something with the theoretical implementation. I know with some built-ins this isn't reliable:

// true
document.createElement('blockquote').constructor === document.createElement('q').constructor;

In my mind it should work because you can't reuse the same class for multiple components, though I feel like I'm missing something.

@bradleyayers
Copy link

I know with some built-ins this isn't reliable

I don't think we need it to work with built-ins — it only needs to work with custom elements.

That being said, I'm interested in some concrete examples where builtins don't work as expected, and which environments are affected.

@treshugart
Copy link
Author

I'm doing some experimentation and if you have access to the diff algorithms, this method works. However, at the moment I'm looking for a way to support different virtual DOM libraries - sort of like a custom element / shadow DOM abstraction - and I'd like to be able to tell the virtual element functions to create an element for a given constructor. Unless all the virtual DOM libraries support doing elem.constructor === Ctor this doesn't seem possible without having a standardised way to get the name that a particular constructor was registered with. Any ideas?

@rniwa
Copy link
Collaborator

rniwa commented Oct 6, 2016

One risk here for v1 is that we have customElements.get and if we're adding a new get method that returns the name given a constructor, then we'll have customElements.get and customElement.getName (or some other name) which would be quite inconsistent. Alternatively, we can make get return a name or a constructor based on argument but that seems rather odd.

@treshugart
Copy link
Author

treshugart commented Oct 6, 2016

Alternatively, we can make get return a name or a constructor based on argument but that seems rather odd.

Yeah, that's probably not a good idea. I think the naming of get is sort of ambiguous anyways. What are we "getting"? In the context of customElements it feels like it should return a "custom element", but it returns a "custom element constructor". If define() was set(), it would make more sense in terms of CustomElementRegistry. Maybe a rename of get before finalising v1 is a good thing, even if getName() is never implemented. getConstructor() seems logical to me.

@domenic
Copy link
Collaborator

domenic commented Oct 6, 2016

I don't think we should have getName, and we should not disturb the existing method names even if we add it.

@rniwa
Copy link
Collaborator

rniwa commented Oct 6, 2016

I don't think we should have getName, and we should not disturb the existing method names even if we add it.

Perhaps we need to get a tie breaker from Microsoft or Mozilla. @annevk, @travisleithead ?

@annevk
Copy link
Collaborator

annevk commented Oct 6, 2016

I think having get for the common case is fine, even if we add getName later. get coupled with its argument is clear.

@annevk
Copy link
Collaborator

annevk commented Feb 17, 2018

This relates a bit to the algorithms discussed in whatwg/html#3452. Given that the browser needs them I'm even more convinced that it's reasonable to expose these.

@annevk annevk added the v2 label Feb 17, 2018
@annevk
Copy link
Collaborator

annevk commented Feb 17, 2018

(Although if we want to support what the browser supports it'd have to return a list...)

@matthewp
Copy link

Maybe there's another feature that makes the constructor-to-localName mapping worth doing aside from just this; for example constructable built-in elements, new HTMLParagraphElement().

@rniwa
Copy link
Collaborator

rniwa commented Oct 24, 2019

Maybe there's another feature that makes the constructor-to-localName mapping worth doing aside from just this; for example constructable built-in elements, new HTMLParagraphElement().

That doesn't necessitates a mapping from constructors to local names in WebKit. (I'm gonna avoid going into technical details on why because that's highly engine specific).

@rniwa
Copy link
Collaborator

rniwa commented Mar 24, 2020

At spring virtual f2f, we discussed that:

  • There are many developers asking for this API for custom element constructors.
  • Nobody has raised concrete use cases for builtin elements.
  • Confirmed that Apple still doesn’t want builtin elements to be supported, and Google has historically opposed having this API without any builtin elements; @mfreed7 took an action item to follow up with @domenic about this objections.

@annevk
Copy link
Collaborator

annevk commented Mar 25, 2020

FWIW, I no longer like overloading get() as the return values will be different. That's too weird. getName() seems fine. It does seem like that would have to return a dictionary or some such to account for customized builtins (and perhaps the namespace).

@rniwa
Copy link
Collaborator

rniwa commented Mar 25, 2020

FWIW, I no longer like overloading get() as the return values will be different. That's too weird. getName() seems fine. It does seem like that would have to return a dictionary or some such to account for customized builtins (and perhaps the namespace).

Perhaps an alternative design to return whatever arguments being passed to define like getDefinition.

@mfreed7
Copy link

mfreed7 commented Apr 3, 2020

At spring virtual f2f, we discussed that:

  • There are many developers asking for this API for custom element constructors.
  • Nobody has raised concrete use cases for builtin elements.
  • Confirmed that Apple still doesn’t want builtin elements to be supported, and Google has historically opposed having this API without any builtin elements; @mfreed7 took an action item to follow up with @domenic about this objections.

Per my action item, I spoke to @domenic about this issue. He and I do agree that if we only support custom elements with this API, we're increasing the number of differences between custom elements and built-in elements. We have built-in elements, customized built-in elements, and autonomous custom elements. If I'm handed a constructor, it might or might not work with this new API, depending on what it is, and that feels pretty wrong. @rniwa - I'd be curious to hear whether you disagree with this. In the past, I've seen you arguing pretty strongly for platform consistency. But here you're arguing for inconsistency between custom elements and built-in elements. If the argument is just that the proposed API is on CustomElementRegistry, then perhaps we could move it somewhere more generic like Document?

Having said the above, I do see the obvious developer interest in this capability. We generally support this API, and want to move forward with an interoperable solution. So while we'd prefer to support all constructors, it seems that most/all use-cases center around custom element constructors, so we can go along with an API that only supports custom elements for now. Hopefully it would be defined in such a way that allows expansion later to support built-ins?

@annevk
Copy link
Collaborator

annevk commented Apr 3, 2020

@mfreed7 custom elements have a 1:1 mapping, builtins do not. What kind of API do you envision?

@rniwa
Copy link
Collaborator

rniwa commented Apr 6, 2020

Per my action item, I spoke to @domenic about this issue. He and I do agree that if we only support custom elements with this API, we're increasing the number of differences between custom elements and built-in elements.

That ship has sailed long ago when Google insisted that lifecycle callback can't be sync and that we support non-sync / upgrade for custom elements. Custom elements behave noting like builtin elements today and probably never will given the Web compat.

We have built-in elements, customized built-in elements, and autonomous custom elements. If I'm handed a constructor, it might or might not work with this new API, depending on what it is, and that feels pretty wrong.

That's exactly what customElements.get does for a custom element name.

@rniwa - I'd be curious to hear whether you disagree with this. In the past, I've seen you arguing pretty strongly for platform consistency. But here you're arguing for inconsistency between custom elements and built-in elements. If the argument is just that the proposed API is on CustomElementRegistry, then perhaps we could move it somewhere more generic like Document?

It would definitely be more consistent to have customElements.getName which returns the name of a custom element for a constructor given we already have customElements.get which implements the inverse function.

To begin with, this will be an API on an interfaced named CustomElementRegistry. Why we need to care about builtin elements at all in API about custom elements? The point of CustomElementRegistry is to query and work with custom elements, not builtin elements.

Having said the above, I do see the obvious developer interest in this capability. We generally support this API, and want to move forward with an interoperable solution. So while we'd prefer to support all constructors, it seems that most/all use-cases center around custom element constructors, so we can go along with an API that only supports custom elements for now.

Given there have been no concrete use case presented for retrieving builtin elements' name given their constructors, I don't think supporting builtin elements make sense.

@annevk
Copy link
Collaborator

annevk commented Apr 7, 2020

@mfreed7 how would a global API for both builtin and custom elements work for scoped registries? Or are scoped registries not something Google-at-large is advocating for?

@mfreed7
Copy link

mfreed7 commented Apr 8, 2020

@annevk:

custom elements have a 1:1 mapping, builtins do not. What kind of API do you envision?
how would a global API for both builtin and custom elements work for scoped registries? Or are scoped registries not something Google-at-large is advocating for?

I agree that this isn't straightforward. For scoped registries, since they're scoped to the shadow root, I would think that whatever API we develop for builtins should "just work" there also. But I suppose that depends on the details of the API. Anyway, since we're okay moving ahead without support for builtins, we can punt these questions until later. I should point out that "Google-at-large" is a big place with differing opinions.

@rniwa:

Given there have been no concrete use case presented for retrieving builtin elements' name given their constructors, I don't think supporting builtin elements make sense.

Ok. We are ok moving ahead without builtin support for now, given the strong developer demand for this feature.

@annevk
Copy link
Collaborator

annevk commented Apr 9, 2020

I should point out that "Google-at-large" is a big place with differing opinions.

Sure, but in the end what matters is what you're planning to ship in Chrome and figuring that out internally would save us all a lot of time.

@mfreed7
Copy link

mfreed7 commented Apr 10, 2020

I should point out that "Google-at-large" is a big place with differing opinions.

Sure, but in the end what matters is what you're planning to ship in Chrome and figuring that out internally would save us all a lot of time.

I can't guarantee a schedule for getting this change implemented in Chromium, but you can count us as supportive.

@justinfagnani
Copy link
Contributor

We pretty regularly get requests to provide a way to get a tag name from a constructor in Lit, and I'm pretty reluctant to add anything because of how specific to Lit it would be. Any movement here?

@annevk
Copy link
Collaborator

annevk commented Jul 19, 2021

If I'm reading the last set of comments correctly, an addition of customElements.getName() would have support from Chromium and WebKit. Someone would have to do the specification and testing work required though. (This would not return the local name, but the name of a custom element, to be clear. I doubt we would get consensus on local name given that WebKit does not support the is attribute.)

I think Gecko can also be supportive of this. (I also agree with @domenic now that overloading this with get() is weird. Not sure why I advocated for that in the past…)

@justinfagnani
Copy link
Contributor

It seems like there may be rough consensus here now on a new method customElements.getName(), am I reading the thread correctly? Do we have anyone with time to write up the spec change and make WPT tests?

@rniwa rniwa changed the title Is there a way to retrieve the localName from a custom element constructor? registry: Is there a way to retrieve the localName from a custom element constructor? Apr 20, 2023
@rniwa rniwa changed the title registry: Is there a way to retrieve the localName from a custom element constructor? registry: Add an API to retrieve the localName given a custom element constructor Apr 20, 2023
keithamus added a commit to keithamus/html that referenced this issue Apr 21, 2023
In CustomElements v0 you could use the `.name` field to get the defined
tag name, but CustomElements v1 does not offer such a field. `localName`
can be used within an instance but this prohibits use in, for example,
static methods.

This was discussed in the WCCG F2F, tracked in
WICG/webcomponents#566. The conclusion of the
F2F was to write up a spec change and WPT tests.
@keithamus
Copy link

I threw together WPT tests (web-platform-tests/wpt#39640) and the spec (whatwg/html#9195) as well as filed implementer bugs in Chromium, Firefox and Webkit. I'm also happy to try implementing this in those browsers if there's interest from implementers.

zcorpan pushed a commit to whatwg/html that referenced this issue Jun 30, 2023
In CustomElements v0 you could use the `.name` field to get the defined
tag name, but CustomElements v1 does not offer such a field. `localName`
can be used within an instance but this prohibits use in, for example,
static methods.

This was discussed in the WCCG F2F, tracked in
WICG/webcomponents#566. The conclusion of the
F2F was to write up a spec change and WPT tests.
rubberyuzu pushed a commit to rubberyuzu/html that referenced this issue Jul 20, 2023
In CustomElements v0 you could use the `.name` field to get the defined
tag name, but CustomElements v1 does not offer such a field. `localName`
can be used within an instance but this prohibits use in, for example,
static methods.

This was discussed in the WCCG F2F, tracked in
WICG/webcomponents#566. The conclusion of the
F2F was to write up a spec change and WPT tests.
rubberyuzu pushed a commit to rubberyuzu/html that referenced this issue Jul 21, 2023
In CustomElements v0 you could use the `.name` field to get the defined
tag name, but CustomElements v1 does not offer such a field. `localName`
can be used within an instance but this prohibits use in, for example,
static methods.

This was discussed in the WCCG F2F, tracked in
WICG/webcomponents#566. The conclusion of the
F2F was to write up a spec change and WPT tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests