-
Notifications
You must be signed in to change notification settings - Fork 376
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
Anonymous Custom Elements #1074
Comments
Firstly I want to say great job on thinking through an idea like this! Thanks for presenting it, I hope we get to see more ideas from you. On the surface this might look like a reasonable API, but from an implementation standpoint this would be a significant amount of work; the class is a backing object for the internal "Custom Element Definition", which gets created and attached to the backing class when Some things that would be prevented or made significantly more difficult by an anonymous HTML element:
Given the amount of work involved on the implementation side (very large and lots of careful refactors required) I wonder if it would be worthwhile? How problematic is giving a name to an element today? Is it so bad that it warrants such a large amount of work and potentially making other APIs more awkward to use? |
If the tag name doesn't matter, then we should be able to use a generated tag name to fit in with all the current custom element APIs. Is there problem with userland generated APIs? Is it still the possibility of name collisions? |
Thank you for the encouragement @keithamus . I have a couple of follow up questions/thoughts.
I was afraid that from an implementation perspective that this might not be feasible. I might have made it harder for myself by using custom element in the title? If we consider
I think that this is actually a fine compromise. The primary use case would be as an alternative to scoped element registries. So in practice you would know what the element should be. Anonymity would not be the primary goal so the name is probably not a perfect fit.
I am not certain I understand what the implication is, or maybe what this means. Could you elaborate?
I don't really understand how scoped registries would affect this. It seems like the anonymous element would be same regardless of what exists in a custom element registry (scoped or global)? Would this be a consequence of how this feature could be implemented? The intention is that a
In my mind this would not be stranger than
In my mind yes, it could be worth it. I don't pretend to know how such a thing could be implemented and what the actual cost |
I guess that is one approach that still allows for a DOM-only API. Is finding an appropriate tag name non-trivial? In particular if it supposed to be compatible scoped registries too. Maybe a helper on // my-example.js
customElements.define(customElements.randomName(), MyExampleElement);
export class MyExampleElement extends HTMLElement {
constructor() {
super()
const root = this.attachShadow({ mode: 'open' })
root.innerHTML = '<span>example</span>'
}
}
// index.js
import { MyExampleElement } from './my-example.js'
document.body.appendChild(new MyExampleElement ()) It also has the major drawback that we don't get a way to access this element via HTML. We could do customElements.define(customElements.randomName(), MyExampleElement);
customElements.define('my-example', class extends MyExampleElement {}); But as you note that opens up the door to naming collisions. The idea of creating a dependency to a specific ES module was meant to sidestep any issues with naming. |
If you want to do the things a custom element does - such as having the callbacks (
Personally I don't think it is a fine compromise. It makes for inconsistent modelling of APIs. It's very strange to not be able to select for an element because it's missing or otherwise has a generic tag name; it makes for an inconsistent API where almost everything works except for this one new thing. Selling this idea to implementers might be a struggle.
Implementing such a change could take months of coding, if it were even viable. Analysis would need to be done, each API would need to be assessed and altered. That's if we can get consensus with all the implementers, which alone could take years to come to consensus with all the changes. With the cost of the change should come the scale of the benefit. What comes to mind is a feature like customisable select; a large project that requires significant refactors and has taken years of work. Customisable select has extremely well motivated tangiable benefits though (accessibility, avoiding massive duplication of efforts, etc). So the challenge with this proposal is if you're proposing a roughly eqiuvalent amount of work, can you advocate that it'll give a roughly equivalent amount of benefit? Having been in a few conversations around standards, I imagine some questions you'd need to answer:
You could probably use function getAnonymousName() {
return 'anonymous-' + crypto.randomUUID()
}
customElements.define(getAnonymousName(), MyExampleElement); I think it would be interesting to find evidence of people using patterns like this (generating random names) which could provide some evidence to this feature being useful. Of course, if you find little or no evidence, it might be a sign that this isn't a feature that people really need or that it really solves a problem for people. It's an interesting idea with some great out of the box thinking, but browser vendors, in general, want to prioritise ideas that have a good chance of broad adoption that improves the lives of web users; which is their success metric. They're unlikely to pick ideas that offer speculative/specuous returns. I don't want to discourage you from coming up with many more excellent ideas, I hope you don't see this as me pooh-poohing this one. Just trying to offer some constructive feedback. |
I have personally heard of people using the generated tagname approach. It definitely works, but has one big limitation in that generating a name for your root component doesn't generate names for the custom elements it in turns uses. It's often a bit trickier to use generated names in template systems and CSS, and if you use 3rd party dependencies, they won't be using the generated name strategy. Generated names would benefit greatly from scoped element registries, so that you use a generated name for the root element that may appear in the main document, and it in turns uses scoped registries for its dependencies. |
Even with scoped custom element registries this would be a potential way to deal with moving elements between roots as I've suggested before. |
@keithamus Thank you for the in depth reply. It provides the context I need to understand what I am asking about. Being new to this space there is a lot arcana to try and figure out.
I have personally done random tag name generation before. The primary use case was as in order to provide a wrapper for frameworks to use scoped elements. Assume I have the component This effect is only amplified by import maps since I then can into the situation that multiple bundles can use the same ES module and constructor so we don't only get tag name collisions but also constructor collisions in the registry. So I'd essentially generate (JSX for the example) import { SomeElement as SomeElementBase } from '@elements/some-element.js'
custom.elementsDefine('some-element-1234', class extends SomeElementBase {})
export function SomeElement() {
return <some-element-1234></some-element-1234>
} This combined with import maps can create essentially ESM scoped web components. At the same time this is sort of defeats the point of using web components in the first place since we are forced to produce framework wrappers. The abstract need without discussing specifics would be to import a custom element constructor into an ES module and attach an instance of that to the DOM. We can always generate tag names at each call location: // my-example.js
export class MyExampleElement extends HTMLElement {
static getTag() {
const tagName = `my-example-${crypto.randomUUID()}`
customElements.define(tagName, class extends MyExampleElement {})
return tagName
}
}
// index.js
import { MyExampleElement } from './my-example'
const tagName = MyExampleElement.getTagName()
const myExample = document.createElement(tagName)
// ... All that being said @justinfagnani makes a good point regarding generated names
This would probably require some framework specific wrapper regardless, rendering the entire concept invalid. @Jamesernator I assumed this had been brought up before. I was a little hesitant to raise an issue but figured that there are no stupid questions :) |
I created a similar proposal in the past: https://github.com/adrianhelvik/anonymous-custom-elements |
DCE syntax from <custom-element>
👋<!-- what ever payload in emplate -->
</custom-element>
<custom-element src="puzzled.svg"></custom-element>
<custom-element src="lib.html#common-menu"></custom-element> live example # 2,4,6,7,8,9,10 |
I would like to discuss the possibility of using custom elements without requiring the custom element registry.
Overview
Allowing a custom element to be used without a custom element registry allows us to sidestep a lot of the issues around the global registry that are discussed in #716. Instead of introducing a new registry we allow a per-element definition.
In order to get the conversation started I propose the working name of anonymous custom elements. The primary drive is to couple custom elements with ESM modules. This would require no changes to the DOM-api as we can simply use a constructor directly.
The HTML for this could be
This new
<anonymous>
element would keep track of the module that defines the custom element in the propertymodule
and the constructor in a propertydefinition
. In the example abovemodule
would be the value ofimport.meta.url
anddefinition
the local name ofMyExampleElement
in that module.These two would also allow for an HTML API.
This would load the ESM module
./my-example.js
and use the constructor defined from that module. A similar thing could be done via DOMBenefits
Drawbacks
I am happy to explore this idea with you all. This is my first issue so please guide me if something is missing or is formatted incorrectly.
The text was updated successfully, but these errors were encountered: