Closed rektide closed 8 years ago
It appears to me that all these transpilers that lowers ES6 to ES5 need to be fixed to not break subclassing, and that's the only remaining issue here.
subclassing builtins + species are overall broken, yes.
Dude, this is just insane. Reading this thread I have more questions than answers. I know, this comment is long and probably belongs on a personal blog instead of a github issue, but let me explain where I come from:
I'm developing front end things for four years now and the moment I started, AngularJS was the biggest thing for beginners, but React became more popular over time. The last few times I worked with AngularJS I really hated it and it's size and it's slowness and it's shrinking community. So I thought: "Hey, let's try React, right?" Unfortunately the way things work in React are just to complex or weird for me to comprehend, it seems. Also, I thought the whole JSX thing is unnecessary. I mean, since we are moving to creating everything in .js files, why do we still use html-ish syntax for it instead of directly creating elements with the DOM APIs we have? Why do we use something like styled components with literally the same CSS syntax instead of just doing it the Javascript-camelCase way (element.style
)? There is so much parsing and transpiling going on in the background, I don't know why anyone ever thought, this is the way we should make web components in Javascript in the future. To be fair, I don't know how much it impacts the performance, but even just from a logical perspective, why don't we really only use Javascript?
So, although I really don't have the time for it, I started "newh" (never ever write html), a framework for creating components and elements only using Javascript syntax. And guess what, I wouldn't write this comment, if it was simple.
My final question is now: In a sane javascript environment, like es6+babel, what is the fastest and/or preferred modern way to create custom HTMLElements
and append it to any other HTMLElement
when needed? I'd really love to use es6 classes, but at the moment it's just not possible, it seems. You have to use the old document.createElement
which does some weird HTMLUnknownElement
stuff that seems unsafe and that I don't quite understand.
My dream is this:
import { Component } from "newh";
import myChildComponent from "./my-child-component.js";
const myParentComponent = new Component({
tag: "my-parent-component",
attrs: {
id: "my-id",
class: "my-class"
},
style: {
color: "blue",
backgroundColor: "red"
}
}, [myChildComponent]);
document.body.appendChild(myParentComponent);
where the Component
constructor takes two arguments, a general settings object and a single child component or an array of child components. The output would be:
<html>
<head>
</head>
<body>
<my-parent-component
id="my-id"
class="my-class"
style="color: blue; background-color: red;"
>
<my-child-component>
</my-child-component>
</my-parent-component>
</body>
</html>
To my understanding, something like this is only possible with document.createElement()
and element.appendChild()
, and not with extending the HTMLElement
class. And with "possible" I mean possible while using babel to support older browsers. Imho, this isn't a babel problem. Babel mostly transpiles correctly. There's just a lack of tools and libraries for web components focusing on normal environments. It doesn't matter if this cool new standard is implemented in the newest of the newest browser versions, I'm still on Vivaldi (chromium v64) because the newest official chromium 32bit build for Linux is 1 year old and on v62, so this really is the newest browser I could get for this machine. Now consider the users we develop for, a lot of them have even older browsers and don't even know that there is a way to activate experimental features on some weird chrome:flags page.
This whole thing is just frustrating. I really like the idea of web components and doing more things on the Javascript side, but the way it is presented to us developers is just miserable. Like "Hey, we have a cool new standard/concept of doing things on the web here! Go and use it, it's super cool! Oh and yeah, for the last 5 years we didn't came up with any usable universal polyfill, shim or framework to actually work with it. Lol."
Now you could say: "Why arent you trying to do a polyfill then?" Well, then please first update the damn docs for these web component and customeElements
and HTMLElement
APIs. There is not even one useful page to learn what you can and cannot do and what you should be able to do. Everything we have are some small examples, using HTML code to implement components. Things are really moving too fast here. As always in the software world. But this time it's just crazy how little the creators and implementers of this standard/concept care, to make this transition as easy as possible.
My last few days were just horrible because of this. I thought I could quickly dive into this, but I can't. No one with a normal brain can. It's just too messy right now. Seems like I will use AngularJS for another year, or at least until this situation gets way better than it is now.
Godspeed, please don't mess it up, peace.
@nnmrts you can use the V0 poly described in here, or use the polyfill that Google AMP sponsored and used since the beginning to bring Custom Elements everywhere years ago (plus AFrame, StencilJS, etctera).
Once you have basics portable, you can easily create that newh
thing you want.
Yet the direction of W3C should be to promote modern standards, not just old conventions for libraries authors. We have too many libraries already and all we need is better, and wider support, of native features.
@nnmrts it's a little complicated at first, but all the tools are currently available.
I've gotten my custom elements working in IE 10 (v1 elements using custmElements.define
). What I do is transpile the modern code using Babel 6 along with babel-plugin-transform-builtin-classes, and for the Web Component polyfills I use webcomponents.js. Note that Babel 7 will feature that transform in core, but it hasn't worked for me yet, so I recommend sticking with Babel 6 for now.
It all works great, my elements work in IE 10 and Chrome 65, as well as relevant versions of all the other browsers, all with a single build.
By the way, React offers the ability to dynamically morph your DOM. This is something you don't get with vanilla Custom Elements. You'll end up doing in plain JS what React/Angular/Vue and other view layers already solve for you. In your example above, you created a static DOM, but that example doesn't morph the DOM (declaratively or even imperatively) based on state changes. There's real value in these frameworks.
Furthermore more, Vue and React can be compiled at build time, and at runtime the DOM manipulation is pure JS under the hood, no parsing at runtime.
Adding a plug for Ractive, with great Mustache/Handlebars templates. We've mashed up Ractive with the webcomponents polyfill to give us a productive environment to build our apps. Kind of a lighter weight Polymer -- we call it Monomer.
On Tue, Feb 27, 2018, 9:51 PM Joe Pea notifications@github.com wrote:
By the way, React offers the ability to dynamically morph your DOM. This is something you don't get with vanilla Custom Elements. You'll end up doing in plain JS what React/Angular/Vue and other view layers already solve for you. In your example above, you created a static DOM, but that example doesn't morph the DOM (declaratively or even imperatively) based on state changes. There's real value in these frameworks.
Furthermore more, Vue and React can be compiled at build time, and at runtime the DOM manipulation is pure JS under the hood, no parsing at runtime.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/w3c/webcomponents/issues/587#issuecomment-369103751, or mute the thread https://github.com/notifications/unsubscribe-auth/ACAWh4zMnr7Do5r4oSse4K4Rv62Kw5B7ks5tZL8rgaJpZM4KXuh2 .
@trusktr
Furthermore more, Vue and React can be compiled at build time, and at runtime the DOM manipulation is pure JS under the hood, no parsing at runtime.
Oh, okay, thank you for that information, I didn't know that. So, I kinda get it now, but personally I still wish to have a direct javascript-only solution of doing these things. I thought that is the great nice thing of these new APIs. Welp, nevermind. :D
(if there is something like a javascript-only thing out there, please let me know :heart:)
@nnmrts hyperHTML is an example of 100% JS to morph/manipulate/update/react-to DOM through template literals. It also has light declarative components or HyperHTMLElement companion for CE. This is not the right place for these kind of discussions though, feel free to DM (open) me in Twitter, if needed.
One last thing, here's a non-exhaustive list of view layers, some of which are pure JS.
Btw, you can use pure JS with React, Vue, etc. Just don't compile, don't write JSX (HTML), and instead write stuff like React.createElement('div')
in a React render function. See React without JSX, that's pure JS.
Since the class
syntax currently has to be used to create custom HTMLElements, unless using Reflect.construct
, but does not yet support extending HTMLElements with additional behaviour (since we want to avoid the creation of HTMLElements with no special behaviour). It leaves us with a mixed syntax code base that is quite ugly to look at and more difficult to read.
For example I still have code running that uses the following syntax which still works, but has one foot out the door:
var TestButton = document.registerElement('test-button', {
prototype: {
__proto__: HTMLButtonElement.prototype,
createdCallback: function() {
this.addEventListener("click", function(event) {
console.log('test');
});
},
},
extends: 'button',
});
<button is='test-button'>test</button>
Whereas this does not:
class TestButton extends HTMLButtonElement {
constructor() {
super();
this.addEventListener("click", function(event) {
console.log('test');
});
}
}
customElements.define("test-button", TestButton, { extends: "button" });
Warning: At time of writing, no browser has implemented customized built-in elements (status). This is unfortunate for accessibility and progressive enhancement. If you think extending native HTML elements is useful, voice your thoughts on 509 and 662 on Github.
What I really miss in the whole journey of Web Components - which lasts for more than 7 years now - is a holistic approach to software engineering. What means holistic?
relating to or concerned with wholes or with complete systems rather than with the analysis of, treatment of, or dissection into parts
This practice of forcing ES6 class
syntax really breaks this reasoned principle of holistic software. Guys if you design software API's this means you have to think about all possible consequences, various browsers, various JS engines, polyfills, transpilation, etc. etc. Why aren't you applying a holistic approach? I mean Web-Components aren't just a new JS library, they are a whole set of new browser APIs and have to work reliable.
It belongs to professional software engineering, it's part of your job, I really don't understand this standard of work.
Another product of yours I really disagree is releasing the <template>
spec as living standard without any standard template feature like interpolation, conditionals, loops, partials you name it.
I mean you can find the definition of template in any dictionary, and there are lots of good examples out there in various programming flavors of what a template is. And which product do you release? You just provide a plain <template>
tag and call it finalized living standard. I admit it would be an awesome geek joke at late night comedy. Unfortunately we (web developers) have to work with it.
On the other side I thank you, this way I get costumers asking me to poke with their problems, problems which could be avoided though...
If you don't change your approach, get properly organized and make sure to understand the technical words, your project of web components will fail broad adoption and leads to blog posts like: https://dmitriid.com/blog/2017/03/the-broken-promise-of-web-components/
@AndyOGo I agree, things are not ideal how they are now. It's possible for implementors to make Custom Elements work with ES5 classes (anything is possible, it is just a collection of bits), but they choose to push the problem to user space. It is opposite of what should be: hard stuff solved at a lower level so everyone at the high level (users who know ES5, not just ES6+) can benefit.
Honestly, I use Web Components not because I like how the component system is implemented, but because I want the end result to be components that can be used everywhere, so I just put up with it. 😕
To make matters worse, I made myself a tool, lowclass, which let's me have "protected" and "private" members in my classes, and it is implemented with ES5 classes. This means I can't use it with Custom Elements v1 without going through great lengths. 😢
This means, I can't use a very useful feature of my tool, which is to make the API of my classes impenetrable, just because I chose not to use class
. 😢 😢 😢
There are problems in the CE implementation that are solved by limiting what language features users can use, but I feel that instead the problems should be solved at a deeper level, so that JavaScript can be used in a free and dynamic way as much as possible in the user space. The dynamic nature of JS what makes it great, let's not take it away!
We should avoid coupling APIs to specific language features as much as possible.
Telling people to use Reflect.construct
is practically the same as telling them to use class
, both limiting and degrading developer experience and limiting what developers can achieve compared to the other options (for example making the Custom Element API work with ES5-style classes).
(Hey, but don't confuse the sad faces with my actual mood, I'm quite happy I get to make cross-platform components after figuring out how to deal with all the caveats 😄 )
It's a bummer we can't use any of the following libs to define our element classes.
These first ones are notable ones that are either popular or have cool features like protected/private members, multiple inheritance, concatenative inheritance, type specifiers for properties, interfaces, etc:
And there's many more (I stopped perusing on page 6 of searching "class inheritance" on npmjs.com):
There are some tools designed for use with native class
:
Did you know there were that many? (I didn't even finish the search on NPM)
It would be great for new APIs not to discount the existing ecosystems, and for native class
to be an option.
We could avoid problems (perhaps the most notorious problem of all new browser APIs):
@trusktr totally agree. Vanilla JS is already fantastic, in fact most people think it's just offers a prototype based paradigm. But JS is so much more, in fact in my opinion it's a multi-paradigm programming language. Which gives your lots of powers to implement your own sugar for object-oriented, functional, reactive you name it style of programming. There aren't many languages out there who gave you this opportunity. And Custom Element V1 just decided to cut into it 😱
Alright, so doing something like
return Reflect.construct(HTMLElement, [], new.target)
with new.target
just doesn't work because new.target
is undefined. This is just a complete mess.
Okay, nevermind, the engine is in fact setting new.target
, but in my case is a subclass was calling the super class with traditional ES5-style constructor.apply
which makes new.target
undefined
.
So my above comment was wrong about new.target
.
But this still shows how much of a pain point all of this is, and how easy it is to encounter problems.
To make HTMLELement
subclasses compatible with plain ES5-style constructors that call their super constructor with .apply()
or .call()
, the HTML engine should allow this:
function MyEl(...args) {
const el = Reflect.construct(HTMLElement, args, new.target)
el.__proto__ = this.__proto__
this.__proto__ = el
// test:
this.connectedCallback() // connected!
console.log( this instanceof HTMLElement ) // true
}
MyEl.prototype = {
__proto__: HTMLElement.prototype,
constructor: MyEl,
connectedCallback() {
console.log(' ----- connected!')
},
}
MyEl.__proto__ = HTMLElement
customElements.define('my-el', MyEl)
const el = document.createElement('my-el')
document.body.appendChild( el )
But the engine gives this error:
Uncaught TypeError: Failed to construct 'CustomElement': The result must implement HTMLElement interface
But everything about the instance created from the MyEl
constructor implements the interface! There's not a good reason it shouldn't work. The engine could call connectedCallback
if it just looks for the method which is there.
Why exactly can't we be allowed to do things like this?
@trusktr you're not returning the right object from the constructor. You're creating an HTMLElement, then implicitly returning MyEl, which is not an HTMLElement. Regardless of setting the prototype and what instanceof
says, it doesn't wrap a native element object like HTMLElement does.
This works:
function MyEl() {
return Reflect.construct(HTMLElement,[], this.constructor);
}
MyEl.prototype = Object.create(HTMLElement.prototype);
MyEl.prototype.constructor = MyEl;
Object.setPrototypeOf(MyEl, HTMLElement);
MyEl.prototype.connectedCallback = function() {
console.log('my-el connected');
};
customElements.define('my-el', MyEl);
document.body.appendChild(document.createElement('my-el'));
Why doesn't setting the prototype like that work though?
There's nothing wrong with the way you're setting up the prototype. What you're missing is return el;
in the constructor.
... never ending story!!!
What you're missing is return el; in the constructor.
I don't think so, because I'm implicitly returning this
which has the correct prototype (el
) in it.
It'd be great if Custom Elements v2
doesn't have this issue, and is not restricted to a subset of JavaScript in terms of how we define classes.
If plain regular ES5 classes can work perfectly with the polyfilled version of Custom Elements v1
, then surely it is possible to make the native version work too. No?
I'm implicitly returning this which has the correct prototype (el) in it.
The prototype isn't what makes a DOM node a DOM node - the JS object has to wrap a real DOM object created by the browser. Changing the prototype only changes the JS side, it doesn't create a DOM object.
I gave you an example that works, just use that.
I don't think so, because I'm implicitly returning this which has the correct prototype (el) in it.
Okay. In your code, el
!= this
so that's the problem. You can't return this
. It's a different object from el
. Because you're not using class constructor. this
doesn't automatically get set to the result of calling super
constructor (you're not even using super
syntax so there's no way for the engine to figure this out).
Someone wanna help with this one? https://github.com/WebReflection/document-register-element/issues/142
I remember when this call about custom elements and class
es was made. I was willing to deal with it. I figured, the spec authors know what they're doing. It won't be a problem.
But it is a problem. It was a problem when y'all did it, it's still a problem today, and as far as I can tell the end is not in sight.
Every non-toy implementation I've attempted to do with custom elements for more than two years has been blocked by some variation of an issue stemming from the fact that custom elements are supposed to use a class
.
I'm basically the lone advocate for open web standards and web components at the companies where I have these experiences. And as the lone engineer on the design systems I'm trying to build, I have very limited time. This is not helping my case and I'm this close to dropping it, possibly for good. I'm becoming unwilling to go out on a limb for web components anymore.
Not to mention we've lost generations of developers by making this too damn hard to use in real life. Someone hears about web components. They hear about the benefits of using the web platform and supporting open standards. They try to use custom elements. They run into a problem, they switch to React, and they have a great experience with it. We don't get a second chance to make a good impression.
@morewry your enemy here has a name, is called Babel, and every bundler using it carelessly.
I wouldn't blame standards right away, but surely I think standards have been blind for a long time.
hyperHTML and lit-html are an attempt to use standards the compelling way for developers, and yet standards don't really listen to devs :man_shrugging:
Still a pretty irrelevant rant on this death thread though, good luck opening a new one.
I get that my problem isn't a bug in the spec, and has to do mainly with Babel's transpilation, but I think it's a valid perspective that the spec created the situation that led to my problem. That's why I want to express I think it was not a good call, especially given the consistent feedback since. This was one of the busiest conversations I've seen in that respect, so I chimed in here.
(I'd certainly open a new one if I thought there was any chance of it getting a different reaction from this one, but...I don't think that.)
@rniwa
Because you're not using class constructor. this doesn't automatically get set to the result of calling super constructor (you're not even using super syntax so there's no way for the engine to figure this out).
Seems like the problem should've been fixed in the engine without changing how JS works on the outside. It is possible!
I find myself here again because now I have a Custom Element defined where the following is happening and is very strange:
import MyElementClass from './my-el'
customElements.define('my-el', MyElementClass)
document.createElement('my-el') // works perfectly
new MyElementClass // TypeError: Illegal constructor
It works great in Chrome, Firefox, Safari, but I get this error in Electron spawned from Karma.
What on earth could be causing createElement
to construct my element properly, yet using new
doesn't work? No one answer that, I'm sure there's a handful of possibilities. The main point is that these sorts of problems are just bad.
Now I have to go spend some unknown amount of time solving a problem that no one should have to solve because the problem shouldn't exist.
The problems are a
... never ending story!!!
@SerkanSipahi ☝️👍
There is definitely an alternate course of history that JS (and HTML) could've taken so that all builtin classes were extendable in ES6+ without requiring any new language features.
@domenic
It's not possible to use custom elements without ES6 classes.
Yes, not possible for JS devs because of how the engine is implemented. But it's technically possible to change the engine implementation. (I'm not saying it is easy, but it is possible!).
I get that my problem isn't a bug in the spec, and has to do mainly with Babel's transpilation, but I think it's a valid perspective that the spec created the situation that led to my problem
@morewry great point!
It works great in Chrome, Firefox, Safari, but I get this error in Electron spawned from Karma.'
That just sounds like a bug in Electron...
That just sounds like a bug in Electron...
Turns out I was goofing up. Load order is important. The following doesn't work:
const ES5Element = function() {}
Reflect.construct(HTMLElement, [], ES5Element) // Uncaught TypeError: Illegal constructor
customElements.define('es5-element', ES5Element)
while the following does:
const ES5Element = function() {}
customElements.define('es5-element', ES5Element)
Reflect.construct(HTMLElement, [], ES5Element)
Perhaps the error messages from the browsers could be more helpful.
For example Uncaught TypeError: Illegal constructor, HTMLElement. Did you forget to define a custom element before calling it with 'new'?
instead of just Uncaught TypeError: Illegal constructor
.
The reason I thought it worked in other browsers besides Electron was because I was either writing markup, or using document.createElement
to create the elements, which works and the engine can upgrade them later. I was using new
in Electron before defining the elements.
Would it be possible for the engine to special-case new MyElement
to behave similarly to document.createElement
?
@trusktr
To help you ignore order you should always construct you elements as soon as they resolve by using customElements.whenDefined(name);
:
const ES5Element = function() {}
customElements.whenDefined('es5-element').then(function() {
Reflect.construct(HTMLElement, [], ES5Element) // will never depend upon order
});
customElements.define('es5-element', ES5Element)
FWIW, WebKit / Safari generates a slightly more helpful error message: TypeError: new.target does not define a custom element
.
I am able to use Reflect.construct to migrate all my V0 components to V1. But now I am facing issues while I am creating new V1 components using ES6 classes(babelified).
Seems like all children with ES6 classes custom components are getting initialized correctly by the time browser calls connectedCallback
but non class based child components which are built using Reflect.construct
technique are not initialized in the connectedCallback
of parent(which is ES6 class based component). Anyone faced this issue?
Note that in general custom elements are upgraded in the tree order (prefix DFS), and connected & disconnected callbacks are enqueued in the tree order as well so in connectedCallback
, for example, you shouldn't expect to be able to talk to your child elements / nodes. The recommended paradigm is for each child custom element to notify its parent. If you had to observe other kinds of changes or have to accept non-custom element, the recommendation is to use MutationObserver.
The recommended paradigm is for each child custom element to notify its parent.
Is there any implementation or wrapper to handle after my child components are ready and attached? MutationObserver is not something which completes my requirement. At present I am having setTimeOut
in my base component class like following which runs ready
method only once. But this doesn't seem to be correct way.
connectedCallback() {
if (!this.isCustomInitialized) setTimeOut(()=>{this.ready()}, 0);
this.isCustomInitialized = true;
}
ready() {
// write code which runs after children custom components are ready with shadowDOM
}
MutationObserver is not something which completes my requirement
Why? MutationObserver lets you observe when child elements are inserted or removed. That's exactly when you should be running the logic to update your container element.
In general, the idea of all children being ready is flawed since an element can be inserted or removed dynamically.
I got your point of element insertion or removal but there is high probability the MutationObserver runs multiple times if there are multiple children that are active. Anyways I don't think need for lifecycle event after children being ready is a flaw. Even many major frameworks react (componentDidMount), vue (mounted), angular (afterView/afterContent) provides you similar functionality.
Even many major frameworks react (componentDidMount), vue (mounted), angular (afterView/afterContent) provides you similar functionality.
componentDidMount is the same as connectedCallback right? it has no concept of completeness of rendering.
Consider the following example:
// connected to dom =>
<foo-bar></foo-bar>
// is it ready?
// no as it needs to load some language data from an api =>
<foo-bar><p>hey foo</p></foo-bar>
// new we could say it's ready
It seems only you as a component author can make sure you are done done... and you can use whatever means necessary to do so (Promise, Callback, Event, ...)
PS: even more "crazy" example... assuming we take the finished loading of translations as being ready: an element that loads a form from an api which has some translations but also special input elements which need to load their own translations => so you could end up with the "form" being already ready (e.g. translations loaded) but the child elements are not (e.g. translations are not yet loaded) ... so to be truly ready you would need to check for every child elements readiness as well... => we are going down the rabbits hole 🙈
I haven't seen how react works internally much 😁 . I just tried logging componentDidMount
and found that children's componentDidMount
are called before parent's. But I am assuming due to virtual DOM that comes in react life cycle events, componentDidMount
is happening after child DOM ready and connected as well.
Regarding example, true that it is getting complicated. I think that's where frameworks like react or vue won the game over custom elements/webcomponents.
@thecodejack You have to be aware of one major caveat of custom elements, they are asynchronous. React is synchronous.
@daKmoR
So you can't say connectedCallback
is the same as componentDidMount
, there is this big difference of asynchronicity and others like V-DOM vs. the DOM.
I really recommend to carefully study the whole spec to become familiar with all the caveats involved: https://html.spec.whatwg.org/multipage/custom-elements.html
@AndyOGo thx for clearing that up... so for the rendering componentDidMount
will be sync.
However, the example with the loading of translations (via fetch to an external api server) is still valid right? or will componentDidMount
wait until my xhr is done?
@daKmoR You are welcome.
Regarding loading of translations, yes it's still valid.
Normally the workflow is as follows on componentDidMount()
-> fetch()
some data -> then call setState()
-> will trigger componentDidUpdate
, etc.
I really like this interactive diagram for React Lifecycle hooks and would wish similiar for custom elements: http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
Uncaught SyntaxError: Failed to execute 'define' on 'CustomElementRegistry': "ufo" is not a valid custom element name
@xgqfrms that's not a valid custom element name
@WebReflection Thanks a lot, I had fixed that.
<ufo-element>👍 customElements.define("ufo-element", UFO);</ufo-element>
<ufo>👎 customElements.define("ufo", UFO);</ufo>
@domenic @morewry I just want to pop in and say that this is ridiculous. Javascript is a prototype-based language, and to get "consensus" it was necessary to block a critical feature from being possible using the prototype paradigm--and you refuse to fix it when it causes problems because of some sort of politics? It's nice that we have the option to use nice ES6 class syntax... but it's less powerful than raw prototyping and it's simply mind-boggling to me that my search for a solution led me to "there is none, because a committee banned it to force you to code their way even though the core features you're using aren't going anywhere".
Sometimes features are deprecated and things aren't backwards compatible; sometimes poor design introduces obstacles; but "sometimes a committee wants everyone to code a certain way for no particular reason and introduces obstacles to using perfectly supported features in a given context on purpose"? Is Javascript deprecated? Or is there a plan to eliminate prototypes from the language and move to a purely class-based approach--then perhaps add in strong typing and C++ template syntax? If so, that's crazy; if not, then the lack of support for prototype-based custom components is a serious bug.
Is there no way this can be brought up and addressed now that 4 years have passed? If the only reason there's no solution is that the design was intentionally crippled by bureaucratic fiat at a face-to-face meeting half a decade ago, perhaps it could be fixed now?
I guess I will now proceed to implement a bizarre, unreadable, inefficient workaround or introduce some hideous class definitions into my otherwise class-free library, for no reason whatsoever except that "the owners of the internet don't like prototypes".
@sapphous Class syntax is itself part of the “prototype paradigm,” but as others have mentioned, you don’t actually have to use it:
function NoClassSyntaxElement() {
return Reflect.construct(HTMLElement, [], new.target);
// or Reflect.construct(HTMLElement, [], NoClassSyntaxElement), I suppose, if you don’t care about subclassing
}
NoClassSyntaxElement.__proto__ = HTMLElement;
NoClassSyntaxElement.prototype.__proto__ = HTMLElement.prototype;
customElements.define('no-class-syntax', NoClassSyntaxElement);
console.log(new NoClassSyntaxElement().matches(':defined')); // true
This is useful if you need the super reference to be static but need to leave [[IsExtensible]] true (i.e., mimicking the behavior of platform interface constructors that inherit from others). That is pretty niche, mainly of interest for high-fidelity polyfilling stuff, but if for whatever reason you are averse to using class
syntax to define your prototypes and object factories, it could serve you well too, and can be tucked away in a helper function.
Class syntax is a mechanism for defining prototypes declaratively alongside any initialization work involved in minting new instances. It doesn’t prevent manipulating the prototype and its properties; you can still do everything you might do in its absence. There is one (fairly obscure) primitive capability that class syntax has that is not exposed any other way* and there is one (fairly obscure) primitive capability that function syntax has that class syntax doesn’t**, but apart from these two things, they are exactly equal in capability.
Not trying to convince you to use em, just mentioning this because “less powerful than raw prototyping” seems like it might be a misconception (if you meant powerful in the sense of what fundamental capabilities they permit).
Hello, I'd like for there to be an available, working examples of autonomous and customized Custom Elements made without use of the
class
syntax. The Mozilla MDN page for example shows a use of Object.create(HTMLElement.prototype) to create an autonomous custom element on it's Custom Elements page that satisfies this non-class based way of working, however that example doesn't work- it yieldsUncaught TypeError: Failed to execute 'define' on 'CustomElementRegistry': The callback provided as parameter 2 is not a function.
on customElement.define("my-tag", MyTag).What is a valid syntax to use now, for creating autonomous and customized Custom Elements? Might we add some examples of such in to the spec?