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

Anonymous custom elements 匿名自定义组件 #842

Open
masx200 opened this issue Sep 23, 2019 · 18 comments
Open

Anonymous custom elements 匿名自定义组件 #842

masx200 opened this issue Sep 23, 2019 · 18 comments

Comments

@masx200
Copy link

masx200 commented Sep 23, 2019

I want to propose a new proposal to the W3C.

Anonymous custom elements
.
Why do custom components have to define a name? Defining names is easy to conflict. Modern front-end frameworks use virtual doms, and the ability to directly write html is too weak.

Because you can't write functions or objects directly in html

I hope I can use custom elements without define

customElements.define()

In case of using an constructor to create an anonymous custom component directly

var anonymousElement=new class extends HTMLElement{}
document.body.appendChild(anonymousElement)

.

Also want to create anonymous custom components without using document.createElement

Under the current api, you can only randomly define names for custom components.Unable to get all custom components that have been defined

I also wrote a js library that randomly named custom components.

Https://github.com/masx200/custom-elements-random-define

@JanMiksovsky
Copy link

In a recent post about custom element registration, I expressed support for an idea along these lines. Like @masx200, we've had to invent a means to auto-register component classes with generated names. As that post indicates, we almost never use registered component tags in our code — we create all components via their constructors, so registration of a tag for each class is undesirable overhead for us.

It doesn't seem feasible (or even desirable) to actually remove the need for custom element tags, but I wonder whether it would be possible to have invocation of a component constructor trigger auto-registration of that class using a generated tag name if the class isn't already registered. E.g.,

// Can instantiate without explicit registration.
class MyElement extends HTMLElement {}
const myElement = new MyElement(); // works, implicitly registers class

// Can reference localName of implicitly-registered class
const tag = myElement.localName; // "my-element-a87f3ee9" or something

// The generated tag is usable.
const myElement2 = document.createElement(tag);

// Once implicitly registered, a class can't be explicitly registered.
customElements.define('my-element', MyElement); // throws

@justinfagnani
Copy link
Contributor

I think #716 is a much better solution for this. Rather than take a name out of the global namespace, just register the elements you need in your scope. Then you can use whatever name you want, including a generated one (within some constraints, like element collaboration that depends on names).

Emulating registry scopes by generating names is similar to emulating shadow dom by generating class names. It can work in some cases, but a real scope will be much better.

@masx200
Copy link
Author

masx200 commented Oct 8, 2019

In a recent post about custom element registration, I expressed support for an idea along these lines. Like @masx200, we've had to invent a means to auto-register component classes with generated names. As that post indicates, we almost never use registered component tags in our code — we create all components via their constructors, so registration of a tag for each class is undesirable overhead for us.

It doesn't seem feasible (or even desirable) to actually remove the need for custom element tags, but I wonder whether it would be possible to have invocation of a component constructor trigger auto-registration of that class using a generated tag name if the class isn't already registered. E.g.,

// Can instantiate without explicit registration.
class MyElement extends HTMLElement {}
const myElement = new MyElement(); // works, implicitly registers class

// Can reference localName of implicitly-registered class
const tag = myElement.localName; // "my-element-a87f3ee9" or something

// The generated tag is usable.
const myElement2 = document.createElement(tag);

// Once implicitly registered, a class can't be explicitly registered.
customElements.define('my-element', MyElement); // throws

You can try the "JavaScript" toolkit I wrote and randomly define the name.
https://github.com/masx200/custom-elements-random-define

var mycom = class extends HTMLElement {};
const tag =RandomDefine(mycom);
var myele = new mycom();
var mycom = class extends HTMLDivElement {};
const tag =RandomDefine(mycom, "div");
var myele = new mycom();
interface Class {
  new (): HTMLElement;
  prototype: HTMLElement;
}
function RandomDefine(initclass: Class, extendsname?: string): string;

@JanMiksovsky
Copy link

@justinfagnani Scoped registries will be great! But they solve a different problem, no? The focus here is that having to learn and think about registries — whether global or scoped — adds conceptual load. Scoped registries increase (rather than decrease) the knowledge and code required to instantiate an element:

class MyElement extends HTMLElement {}
const myRegistry = new CustomElementRegistry(window.customElements);
myRegistry.define('my-element', MyElement);
const myElement = new MyElement();

[I'm not entirely sure the above would actually work. The examples at #716 show the association of a scoped registry with a shadow root. If that's absolutely required — e.g., to avoid name conflicts with those in the global registry — then even the verbose code above wouldn't be possible to instantiate elements at the document level.]

The main thing I'm trying to convey is that, over the past couple of years, I've come to realize that the number of situations in which I actually care about the tag name is small. In which the tag name is, at best, a side concern, it would be nicer to be able to just create the class and instantiate it.

Addressing this is clearly not essential — we can work around the problem. But looking at other libraries on the web today that export classes, I can usually instantiate those classes immediately; no other bookkeeping is required. I'm just observing that the need to register classes at all makes working with custom element classes more complex.

@TimvdLippe
Copy link

Similar request I posted at whatwg/html#4944

@adrianhelvik
Copy link

adrianhelvik commented Dec 7, 2019

I think this is a wonderful idea. We have to determine how it should affect .innerHTML vs in the element inspector.

What about just not including the element in .innerHTML?

inspector

<div>
  <#MyComponent>
    <span/>
  </#MyComponent>
</div>

innerHTML

<div>
  <span></span>
</div>

@adrianhelvik
Copy link

adrianhelvik commented Dec 7, 2019

I have created a polyfill now and I think that we need a way to separate anonymous elements from named elements. The nicest solution here imo. is this:

class MyAnonymousElement extends AnonymousElement {
  // ...
}

Link to proposal and polyfill: https://github.com/adrianhelvik/anonymous-custom-elements

@annevk
Copy link
Collaborator

annevk commented Dec 7, 2019

So the proposal here is to generate a random local name and avoid the call to define() somehow?

@adrianhelvik
Copy link

Well, technically to not have a name. The random name is there out of necessity. A random name isn't that bad either I guess.

@annevk
Copy link
Collaborator

annevk commented Dec 7, 2019

How would not having a name work? (There's many more APIs than innerHTML that look at it.)

If it's a random name, why does it need to be added to the standard library? Seems easy enough to do yourself.

@TimvdLippe
Copy link

@annevk I would like to do a write-up about the use cases (e.g. reusable component vs application development) and why "anonymous" custom elements make sense for these contexts. However, I will probably get to that after next week because of other work I have to finish next week. So I will be able to answer your question, but with a bit of a delay 😄

@TimvdLippe
Copy link

I started a write-up, but couldn't finish it before the Christmas vacation. Will finish it next year.

@trusktr
Copy link

trusktr commented Jan 25, 2020

An element tag name can also be important in CSS styling. So if the element has no name or random name, it can be hard to target it in any selectors. Is this something that people are okay to sacrifice?

@adrianhelvik
Copy link

@trusktr Good point. Anonymous elements must have display: contents.

@marchbox
Copy link

What’s the difference between an Anonymous Custom Element and a regular ES class?

@adrianhelvik
Copy link

adrianhelvik commented May 25, 2020

It is a DOM node, but can only be created by calling new MyElement(). It bears no semantic meaning and don't even appear in the markup.

I've done some shenanigans to polyfill innerHTML/outerHTML, as you can see if you open the Live preview in my proposal and check out document.body.innerHTML in the console.

PS: Some nasty hacking is involved and void elements are not accounted for.

@marchbox
Copy link

marchbox commented May 25, 2020

To me, the main thing I love about Custom Elements is that it offers a standardized way for authors to extend the browser's functionalities in a declarative way. Just like when you add some form element to your HTML document, you magically get some dynamic functionalities. Adding JS to HTML essentially is to extend the browser's abilities, and I feel being able to do it declaratively is very important, it's the only way we can progressively enhance our webpages.

HTML already has somewhat anonymous elements like div and span, it might make more sense to extend the spec of Custom Built-in Elements, e.g. instead of upgrading a div with is attribute, create an imperative way of upgrading a built-in element to a custom element without requiring an element name, very similar to what we already do with some frameworks:

@Component({
  selector: 'div.my-element`,
})
export class MyElement extends HTMLElement {
  // ...
}

Edit:

I just saw this, it seems very interesting:
https://github.com/lume/element-behaviors

@trusktr
Copy link

trusktr commented Jul 12, 2020

I just saw this, it seems very interesting:
https://github.com/lume/element-behaviors

Oh thanks! I made that. :) That indeed could serve as an alternative in some cases but maybe not what the OP is asking for.

More details (off topic)

That's a little tool I made which is basically an alternative to the is="" attribute (so basically an alternative to custom elements). It allows practically the same thing as custom elements (you write classes with the same life cycle methods), but allows you to have more than one of them associated with an element (which is convenient) without changing the original tag name of the element (for example apply them to <tr> or <td> elements without breaking the DOM parser behavior like you otherwise do with custom elements that don't use the is="" approach which Safari does not want to implement for various valid reasons).


On the main topic, for what the OP is asking, I agree with @justinfagnani that custom element registries scoped to ShadowDOM is a better approach for encapsulation of naming and implementation.

How would anonymous custom elements be represented in HTML? It seems this would make things like SSR (server-side rendering) more difficult to achieve. The more difficult it is, the more specific each solution will be which can cause fragmentation between different custom element libraries that end up having very specific requirements for things like SSR to work properly.

As an example of the opposite, the Declarative-Custom-Elements proposal would bring some standardization to custom elements as far as representation in HTML, which would make it easier to create things like SSR, and make it easier for custom element libraries to have a chance at being compatible with each other (there are still issues, the community would still need to make its own standards on top of that proposal, but the community standards would simpler and smaller).

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

8 participants