[Custom Elements] Should I write v1 Custom Elements in a way backwards compatible with v0 API?
ved for brevity... } attachedCallback() { this.connectedCallback() } // back-compat disconnectedCallback() { // code removed for brevity... } detachedCallback() { this.disconnectedCallback() } // back-compat } classCache.set(elementClass, WebComponent) return WebComponent } Any thoughts? */#!/*JoePea
Re: [Custom Elements] Not requiring hyphens in names.
Thanks for the link! I really believe that we should be allowed to name our elements with any name we wish, and that they should override native elements (that is future proof), and that overriding should be on some type of encapsulated basis (for example, override within a shadow root and it won't affect other code outside the shadow root). On Thursday, April 14, 2016, Takayoshi Kochi wrote: > Just FYI, the idea of allowing non-hyphen elements if they contain > non-ASCII characters > which probably won't collide with future HTML elements was posted in the > discussion: > https://github.com/w3c/webcomponents/issues/239#issuecomment-190603674 > > > On Fri, Apr 15, 2016 at 7:01 AM, /#!/JoePea wrote: > >> On Wed, Apr 13, 2016 at 1:09 PM, Tab Atkins Jr. >> wrote: >> > That means we lose the lingua franca that HTML provides; two >> > independent libraries can't ever depend on the core HTML elements, >> > because the other library might have overridden some of them. >> >> Based on my idea of registering elements on a per-shadow-root basis >> ( >> https://discourse.wicg.io/t/proposal-register-custom-elements-onto-shadowdom-roots/1440 >> ), >> then libraries could use any native or custom elements that they want >> within their shadow roots. Shadow roots would provide encapsulation, >> and element registrations would be scoped to those shadow roots. >> >> There are two possible ways to deal with element registrations on the >> `document`: >> >> 1. Either elements registered on the `document` have effect across >> shadow roots, but shadow roots can override these registrations: >> >> ```js >> document.registerElement('foo-bar', SomeClass) >> // ... >> shadowRoot.registerElement('foo-bar', OtherClass) // takes precedence >> over the document registration. >> ``` >> >> 2. Or, document registrations simply wouldn't cross the shadow root >> boundary. >> >> I personally like the second option, leaving maximum control in the >> hands of the developer, regardless of some global script registering >> an element on the document that may encroach the scope of a shadow >> root. If a shadow root author really wants the same element, there's >> slightly more effort to also register that element with the shadow >> root, but the overall control outweighs this extra effort in my >> opinion. >> >> Then, if we add the ability to override native elements, we'll have >> something powerful, IMO. >> >> ```js >> // file1.js >> import BetterImageWithWebGLFilters from 'better-img' >> document.registerElement('img', BetterImageWithWebGLFilters) >> >> // file2.js >> let s = el.createShadowRoot() >> s.appendChild(document.createElement('img')) // uses native >> HTMLImageElement because document registration stops at shadow root >> boundary. >> >> // file3.js >> import BetterImageWithWebGLFilters from 'better-img' >> let s = el.createShadowRoot() >> s.registerElement('img', BetterImageWithWebGLFilters) >> s.appendChild(document.createElement('img')) // this person wants >> BetterImageWithWebGLFilters in their shadow root >> >> // file4.js >> let s = el.createShadowRoot() >> s.registerElement('div', AwesomeClass) // this does not affect other >> shadow roots or the document, it's contained within the shadow root. >> ``` >> >> This would be awesome I think. It'd allow for a level of encapsulation >> and modularization on a shadow-root basis (which can paired with >> Custom Elements very nicely). >> >> /#!/JoePea >> >> > > > -- > Takayoshi Kochi >
Re: [Custom Elements] Not requiring hyphens in names.
On Wed, Apr 13, 2016 at 1:09 PM, Tab Atkins Jr. wrote: > That means we lose the lingua franca that HTML provides; two > independent libraries can't ever depend on the core HTML elements, > because the other library might have overridden some of them. Based on my idea of registering elements on a per-shadow-root basis (https://discourse.wicg.io/t/proposal-register-custom-elements-onto-shadowdom-roots/1440), then libraries could use any native or custom elements that they want within their shadow roots. Shadow roots would provide encapsulation, and element registrations would be scoped to those shadow roots. There are two possible ways to deal with element registrations on the `document`: 1. Either elements registered on the `document` have effect across shadow roots, but shadow roots can override these registrations: ```js document.registerElement('foo-bar', SomeClass) // ... shadowRoot.registerElement('foo-bar', OtherClass) // takes precedence over the document registration. ``` 2. Or, document registrations simply wouldn't cross the shadow root boundary. I personally like the second option, leaving maximum control in the hands of the developer, regardless of some global script registering an element on the document that may encroach the scope of a shadow root. If a shadow root author really wants the same element, there's slightly more effort to also register that element with the shadow root, but the overall control outweighs this extra effort in my opinion. Then, if we add the ability to override native elements, we'll have something powerful, IMO. ```js // file1.js import BetterImageWithWebGLFilters from 'better-img' document.registerElement('img', BetterImageWithWebGLFilters) // file2.js let s = el.createShadowRoot() s.appendChild(document.createElement('img')) // uses native HTMLImageElement because document registration stops at shadow root boundary. // file3.js import BetterImageWithWebGLFilters from 'better-img' let s = el.createShadowRoot() s.registerElement('img', BetterImageWithWebGLFilters) s.appendChild(document.createElement('img')) // this person wants BetterImageWithWebGLFilters in their shadow root // file4.js let s = el.createShadowRoot() s.registerElement('div', AwesomeClass) // this does not affect other shadow roots or the document, it's contained within the shadow root. ``` This would be awesome I think. It'd allow for a level of encapsulation and modularization on a shadow-root basis (which can paired with Custom Elements very nicely). /#!/JoePea
Re: [Custom Elements] Not requiring hyphens in names.
What if custom Elements simply override existing ones then? ```js shadowRoot.registerElement('div', MyElement) ``` If overriding native elements was documented, it'd be fine. By default, a blank document or shadow root has no elements registered, so would use the native DIV. But, why not let the user define what a is? There could optionally be a warning outputted to console: ``` Warning: DIV was overridden: /path/to/some/file.js:123:4 ``` ^ shows where is happens so an end user can easily find and remove the culprit if that is ever a problem. In JavaScript, we can override many of the things on the global `window` object. It's the developer's job to add (or remove) scripts that modify their `window` properties. Why not give the same level of flexibility with DOM elements? , , etc, can just be treated as global variables, overridable. I don't think that would do too much harm. Being able to override on a shadow-root basic could encapsulate the cases where such overriding is needed. Being able to override native elements would make some interesting use cases possible too, like providing alternate functionality to commonly-used elements without having to touch existing markup of an existing app. For example, AMP's element could just be . TLDR: browsers would still be able to add new native elements, while users would be able to override them. Why not? Allowing this won't break the web (since nothing is currently overridden to begin with). Let's make both browser makers and web app authors live's better as much as possible at the same time. Being able to override elements would be such a compromise: browser developers can add new default elements, while web app authors can choose to use them or not, or to override them. Riot.js likes not requiring hyphens too! /#!/JoePea On Wed, Apr 13, 2016 at 11:53 AM, Nick Dugger wrote: > I personal don't mind the hyphenation requirement for custom elements. Tab > Atkins brings up a great point about ensuring that new elements will be able > to be added to spec without worry of name conflicts with existing custom > elements on the page. It's much more future proof, in my opinion. > > On Wed, Apr 13, 2016 at 1:12 PM, /#!/JoePea wrote: >> >> I personally don't like this limitation. I think Custom Elements would >> be better if we could create elements that have >> , with the possible exception that we can't override the >> native elements. >> >> Based on my previous email about registering elements on shadow roots, >> I think being able to choose any name would make things just cleaner: >> >> ```js >> // --- SomeElement.js >> import MyElement from './MyElement' >> >> export default >> class SomeElement extends HTMLElement { >> constructor() { >> this.root = this.createShadowRoot() >> this.root.registerElement('MyElement', MyElement) // >> or >> >> const frag = document.createDocumentFragment() >> frag.innerHTML = ` >> >> >> ... >> >> >> ` >> this.root.appendChild(frag) >> } >> >> static get observedAttributes() { return [ ... ] } >> connectedCallback() { ... } >> disconnectedCallback() { ... } >> attributeChangedCallback() { ... } >> } >> >> // --- app.js >> import SomeElement from './SomeElement' >> >> // elements registered on the document won't cross into shadow roots >> document.registerElement('SomeElement', SomeElement) >> document.body.appendChild(document.createElement('someelement')) >> ``` >> >> /#!/JoePea >> >
[Custom Elements] Not requiring hyphens in names.
I personally don't like this limitation. I think Custom Elements would be better if we could create elements that have , with the possible exception that we can't override the native elements. Based on my previous email about registering elements on shadow roots, I think being able to choose any name would make things just cleaner: ```js // --- SomeElement.js import MyElement from './MyElement' export default class SomeElement extends HTMLElement { constructor() { this.root = this.createShadowRoot() this.root.registerElement('MyElement', MyElement) // or const frag = document.createDocumentFragment() frag.innerHTML = ` ... ` this.root.appendChild(frag) } static get observedAttributes() { return [ ... ] } connectedCallback() { ... } disconnectedCallback() { ... } attributeChangedCallback() { ... } } // --- app.js import SomeElement from './SomeElement' // elements registered on the document won't cross into shadow roots document.registerElement('SomeElement', SomeElement) document.body.appendChild(document.createElement('someelement')) ``` /#!/JoePea
Re: [Custom Elements] They are globals.
On Mon, Apr 11, 2016 at 4:44 PM, Ryosuke Niwa wrote: > You'd have to instead write it as "new SomeClass(this.shadowRoot)" and then > (1) needs to be modified as: `super(..arguments)` to pass the argument along > to the HTMLElement constructor. For sure, similar to the examples in the GitHub issue. :] React doesn't encourage the instantiation of element classes directly. What if browsers did that too and threw errors whenever someone tried to instantiate a class using `new` that extended from any of the native classes: ```js import {SomeElement} from 'some-library' // this would cause a `TypeError: Illegal constructor` or something let el = new SomeElement() // does not return a constructor shadowRoot1.registerElement('any-name', SomeElement) // only this instantiation method is allowed let el1 = shadowRoot1.createElement('any-name') // we can use the same class in a different component where // is already defined but isn't backed by SomeElement shadowRoot2.registerElement('other-name', SomeElement) let el2 = shadowRoot2.createElement('other-name') // maybe this should throw an error instead of returning // HTMLUnknownElement, otherwise it might hide the obvious // human error instead teaching the developer shadowRoot1.registerElement('a-name', SomeElement) let el3 = shadowRoot2.createElement('a-name') // this would cause an error because el1 was created from // shadowRoot1 so el1 doesn't work in shadowRoot2 shadowRoot2.appendChild(el1) ``` ShadowDOM is still evolving and the Web Component spec hasn't officially settled, so I think there's a good opportunity here for the web to be more helpful by throwing errors and not being ambiguous as for example in `document.createElement('ths-has-a-typo')`. TLDR - ShadowDOM could be the encapsulation of HTML to Custom Elements as JSX is the encapsulation of HTML to React Components (in a morphed way, as obviously the mechanics are different). Here's what a small component might look like: ```js // --- HandyForm.js import AwesomeButton from './AwesomeButton' import { FancyForm, FancyInput } from './FancyForm' export default class HandyForm extends HTMLElement { constructor() { this.root = this.createShadowRoot() this.root.registerElement('', AwesomeButton) this.root.registerElement('', FancyForm) this.root.registerElement('', FancyInput) const frag = document.createDocumentFragment() frag.innerHTML = ` ` this.root.appendChild(frag) } static get observedAttributes() { return [ ... ] } connectedCallback() { ... } disconnectedCallback() { ... } attributeChangedCallback() { ... } } // --- app.js import HandyForm from './HandyForm' // elements registered on the document won't cross into shadow roots document.registerElement('handy-form', HandyForm) document.body.appendChild(document.createElement('handy-form')) ``` - Joe /#!/JoePea
Re: [Custom Elements] They are globals.
I get what you mean about the behaviors defined from classes that exist in the scope, in React. The really great thing about React is the ability to compose a class by using multiple classes to return the render spec. One of React's strong-points at a high level is it offers encapsulation where the components used in the HTML (which don't map directly to the actual DOM result that the browser is finally given) are specific to the component, encapsulated there. The current methods of encapsulation that I see with normal HTML are documents and shadow roots, where shadow roots seem to be closer to React than documents because the top-level HTML that we inspect in the DOM may not be what is actually rendered (like with React), although the implementation is completely different, but at a high level shadow dom has that small similarity with React. So, to make code-reuse as possible as it is with React (due to React's aforementioned encapsulation and use of the JavaScript scopefor it's "HTML elements"), maybe the Custom Element API can take advantage of ShadowDOM instead of documents? What if custom elements can be registered on a shadow-root basis, so that the author of a Custom Element (one that isn't registered by default) can register a bunch of elements that it's shadow root will use, passing constructors that the author code may have imported from anywhere. Then that same author simply exports the custom element (does not register it) for a following author to use. The following author would import that custom element, then register it with the shadow roots of his/her new custom elements, and thus the cycle continues, with registered elements defined on shadow roots on a per-custom-element basis, without those elements ever being registered to some global registry. ``` // library code import SomeClass from './somewhere' export default class AuthorElement extends HTMLElement { constructor() { this.shadowRoot.defineElement( 'authors-should-call-this-anything-they-want', SomeClass ) // ... } } ``` Then, finally, the top-level app author can register the top-most custom elements using an API similar to current, in the top-level namespace (global scope) for the window or document. ``` // app code import AuthorElement from './AuthorElement' document.defineElement( 'any-name-the-app-author-wants', SomeClass ) ``` So, basically, inaddition to global/window-or-document-based elements there'd also be shadow root definitions for encapsulation, since shadow roots are to custom elements as what (slightly stretched) JSX specs are to React components. An important part of the Custom Element API would be that Custom Element authors should never register their Custom Elements globally, only export them for the user of their custom element to register them. The final top-level app author would register only the elements that will be used in the top-level HTML of the app, all other elements already registered in their shadow roots by the authors of each element. Something like this would create a development environment much more similar to React, having encapsulation and making code more re-usable. The only downside of this approach would be the need to manually register elements per-shadow-root, not just importing them (in react, importing is enough, as the JSX uses the JavaScript scope as the element registry). On a side note, interestingly, template string tag functions would let us take advantage of JavaScript scope directly without build tools: ``` import SomeClass from './SomeClass' import OtherClass from './OtherClass' html` <${SomeClass}> <${OtherClass}> ` ``` /#!/JoePea On Mon, Apr 11, 2016 at 11:25 AM, Domenic Denicola wrote: > I think you are being misled by a superficial similarity with React's JSX. > > JSX's `` desugars to `React.createElement(Foo)`, which creates a `` > element with some of its behavior derived from the `Foo` class, found in > JavaScript lexical scope. The `Foo` token has no impact on the DOM tree. > > Custom elements' `` is completely unlike that. In that case, `x-foo` > is a tag name, and a full participant in the DOM tree structure. It affects > CSS selector matching, APIs like querySelector and getElementsByTagName, and > more. It's not just a div. > > As Ryosuke notes, it's very hard to imagine how "scoped tag names" would > work. Both for implementation reasons, like the HTMLElement constructor, but > also for cases like CSS or the DOM APIs, which operate fundamentally on a > global mapping. >
Re: [Custom Elements] More ES6-like API
Thanks Ryosuke! That's looking a lot better. /#!/JoePea On Mon, Apr 11, 2016 at 10:28 AM, Ryosuke Niwa wrote: > That's exactly what we're doing. The latest spec uses ES6 class constructor > to define custom elements. See an example below this section in DOM spec: > https://dom.spec.whatwg.org/#concept-element-custom-element-state > > - R. Niwa > >> On Apr 10, 2016, at 7:58 PM, /#!/JoePea wrote: >> >> It'd be nice if users could define actual constructors, as described here: >> >> https://github.com/w3c/webcomponents/issues/423#issuecomment-208131046 >> >> Cheers! >> - Joe >> >> /#!/JoePea >>
[Custom Elements] They are globals.
Hello, Is it possible to take an approach more similar to React where Custom Elements aren't registered in a global pool? What if two libraries we'd like to use define elements of the same name, and we wish to import these via `import` and not modify the source code of our dependencies? I don't really see the solution yet (if any), since the browser needs to know about the elements in order to make them work. Any thoughts? Is a more encapsulated approach possible? Regards, - Joe -- /#!/JoePea
Re: [Custom Elements] Extension of arbitrary elements at runtime.
Hello Brian The purpose of the motor-scene and motor-node elements is that they will be easy to apply 3D transforms to (and WebGL soon), with easing for example. I suppose a better approach for augmenting and existing DOM could be to simply apply the transforms via selectors instead of trying to apply the behavior via extending with is="". This wouldn't allow custom attributes though, like extending would. I think the best solution, for now, is as you recommended: to add the layers if possible. Thanks for the input! - Joe On Monday, April 11, 2016, Brian Kardell wrote: > > > On Sun, Apr 10, 2016 at 11:11 PM, /#!/JoePea > wrote: > >> The is="" attribute lets one specify that some element is actually an >> extended version of that element. >> >> But, in order for this to work, the Custom Element definition has to >> deliberately extend that same basic element type or else it won't >> work. >> >> It'd be nice if a Custom Element definition could be arbitrarily >> applied to any type of element, with the is="" tag for example, and >> that the element would then be upgraded to the extending type at >> runtime. The custom element could be told what class it is extending >> at runtime in order to perhaps act differently using conditional >> statements. >> >> So, writing defining the element could be like this: >> >> ```js >> let isDynamic = true >> document.registerElement('some-element', { >> createdCallback: function() { >> if (this.typeExtended == 'DIV") >> // ... >> if (this.typeExtended == 'BUTTON') >> // ... >> }, >> }, isDynamic) >> ``` >> >> then using the element could be like this: >> >> ```html >> >> >> >> ``` >> >> What are your thoughts on such a way to extend any type of element at >> runtime? Could it be a way for augmenting, for example, an existing >> app without necessarily having to modify it's markup, just simply >> adding is="" attributes as needed? Would this make things too >> complicated? >> >> The real reason I thought of this idea is because: >> https://github.com/infamous/infamous/issues/5 >> >> There might be a better way, but thought I'd mention it just in case >> it sparks any ideas. >> >> Cheers! >> - Joe >> >> /#!/JoePea >> >> > > Is there a reason that you cannot wrap with fallback? For example, in > your github issue you are given and existing app with markup like: > > > > Hello > > > > and the issue wanted to change it to > > > > Hello > > > > Is there a reason it could it not just be > > > > > Hello > > > > > There isn't really a significant difference between div and motor-scene to > non-supporting browsers. > > > -- > Brian Kardell :: @briankardell > -- /#!/JoePea
[Custom Elements] Extension of arbitrary elements at runtime.
The is="" attribute lets one specify that some element is actually an extended version of that element. But, in order for this to work, the Custom Element definition has to deliberately extend that same basic element type or else it won't work. It'd be nice if a Custom Element definition could be arbitrarily applied to any type of element, with the is="" tag for example, and that the element would then be upgraded to the extending type at runtime. The custom element could be told what class it is extending at runtime in order to perhaps act differently using conditional statements. So, writing defining the element could be like this: ```js let isDynamic = true document.registerElement('some-element', { createdCallback: function() { if (this.typeExtended == 'DIV") // ... if (this.typeExtended == 'BUTTON') // ... }, }, isDynamic) ``` then using the element could be like this: ```html ``` What are your thoughts on such a way to extend any type of element at runtime? Could it be a way for augmenting, for example, an existing app without necessarily having to modify it's markup, just simply adding is="" attributes as needed? Would this make things too complicated? The real reason I thought of this idea is because: https://github.com/infamous/infamous/issues/5 There might be a better way, but thought I'd mention it just in case it sparks any ideas. Cheers! - Joe /#!/JoePea
[Custom Elements] More ES6-like API
It'd be nice if users could define actual constructors, as described here: https://github.com/w3c/webcomponents/issues/423#issuecomment-208131046 Cheers! - Joe /#!/JoePea