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

The is="" attribute is confusing? Maybe we should encourage only ES6 class-based extension. #509

Closed
trusktr opened this Issue May 31, 2016 · 469 comments

Comments

Projects
None yet
@trusktr

trusktr commented May 31, 2016

The is="" API can be confusing, and awkward. For example (in current v1 API):

inheritance currently has to be specified three times:

class MyButton extends HTMLButtonElement {} // 1st time
customElements.define('my-button', MyButton, { extends: 'button' }) // 2nd time
<button is="my-button"></button> <!-- 3rd time -->

But, I believe it could be better:

inheritance specified once, easier non-awkward API:

class MyButton extends HTMLButtonElement {} // 1st and only time
customElements.define('my-button', MyButton)
<my-button></my-button>

But, there's problems that the is="" attribute solves, like making it easy to specify that a <tr> element is actually a my-row element, without tripping up legacy parser behavior (among other problems). So, after discussing in this issue, I've implemented an idea in #727:

mixin-like "element behaviors", without extending builtins

The idea uses a has="" attribute to apply more than one behavior/functionality to an element (is="" can only apply a single behavior/functionality to an element), using lifecycle methods similar to Custom Elements.

For example:

// define behaviors
elementBehaviors.define('foo', class { // no inheritance necessary
  connectedCallback(el) { ... }
  disconnectedCallback(el) { ... }
  static get observedAttributes() { return ['some-attribute'] }
  attributeChangedCallback(el, attr, oldVal, newVal) { ... }
})
elementBehaviors.define('bar', class { ... })
elementBehaviors.define('baz', class { ... })
<!-- apply any number of behaviors to any number of elements: -->
<div has="bar baz"></div>
<table>
  <tr has="foo baz" some-attribute="lorem"></tr> <!-- yay! -->
</table>
<button has="bar foo" some-attribute="ipsum"></button>
<input has="foo bar baz" some-attribute="dolor"></input>

In a variety of cases, this has advantages over Custom Elements (with and without is="" and):

  1. No problems with parsing (f.e. the table/tr example)
  2. No inheritance, just simple classes
  3. Works alongside Custom Elements (even if they use is=""!)
  4. "element behaviors" is similar to "entity components" (but the word "component" is already used in Web Components and many other web frameworks, so "behaviors" was a better fit).
  5. Lastly, "element behaviors" makes it easier to augment existing HTML applications without having to change the tags in an HTML document.

See #727 for more details.


Original Post:

The spec says:

Trying to use a customized built-in element as an autonomous custom element will not work; that is, <plastic-button>Click me?</plastic-button> will simply create an HTMLElement with no special behaviour.

But, in my mind, that's just what the spec says, but not that it has to be that way. Why has it been decided for it to be that way?

I believe that we can specify this type of information in the customElement.define() call rather than in the markup. For example, that very same document shows this example:

customElements.define("plastic-button", PlasticButton, { extends: "button" });

Obviously, plastic-button extends button as defined right there in that call to define(), so why do we need a redundant is="" attribute to be applied onto button? I think the following HTML and JavaScript is much nicer:

<!-- before: <button is="plastic-button">Click me</button> -->
<plastic-button>Click me</plastic-button>
// before: const plasticButton = document.createElement("button", { is: "plastic-button" });
const plasticButton = document.createElement("plastic-button");

The necessary info for the HTML engine to upgrade that element properly is right there, in { extends: "button" }. I do believe there's some way to make this work (as there always is with software, because we make it, it is ours, we make software do what we want, and this is absolutely true without me having to read a single line of browser source code to know it), and that is="" is not required and can be completely removed from the spec because it seems like a hack to some limitation that can be solved somehow else (I don't know what that "somehow" is specifically, but I do know that the "somehow" exists because there's always some way to modify software to make it behave how we want within the limitations of current hardware where current hardware is not imposing any limitation on this feature).

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic May 31, 2016

Contributor

This was previously discussed at length in the other bug tracker, and it's explained in the spec in the sentence above that:

Customized built-in elements require a distinct syntax from autonomous custom elements because user agents and other software key off an element's local name in order to identify the element's semantics and behaviour. That is, the concept of customized built-in elements building on top of existing behaviour depends crucially on the extended elements retaining their original local name.

Contributor

domenic commented May 31, 2016

This was previously discussed at length in the other bug tracker, and it's explained in the spec in the sentence above that:

Customized built-in elements require a distinct syntax from autonomous custom elements because user agents and other software key off an element's local name in order to identify the element's semantics and behaviour. That is, the concept of customized built-in elements building on top of existing behaviour depends crucially on the extended elements retaining their original local name.

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr May 31, 2016

@domenic That quote that you referred to makes me think exactly the same thing as I just wrote:

The necessary info for the HTML engine to upgrade that element properly is right there, in { extends: "button" }. I do believe there's some way to make this work (as there always is with software, because we make it, it is ours, we make software do what we want, and this is absolutely true without me having to read a single line of browser source code to know it), and that is="" is not required and can be completely removed from the spec because it seems like a hack to some limitation that can be solved somehow else (I don't know what that "somehow" is specifically, but I do know that the "somehow" exists because there's always some way to modify software to make it behave how we want within the limitations of current hardware where current hardware is not imposing any limitation on this feature).

You're pointing to something that exists in a spec, but I'm saying it doesn't have to be that way, specs can be modified. There is a solution (I don't know what that solution is specifically yet, but I know that the hardware on any computer that runs a browser nowadays isn't a limitation, and the only limitation is in the software we write, which can be modified). I simply can't understand why an HTML engine must need <button is="super-button"> (besides the fact that current algorithms require that) when clearly there's a JavaScript way to make the HTML engine understand that <super-button> means the same as <button is="super-button"> due to the fact we're defining that in the call to customElement.define.

I wish I had the time and resources to read the Chromium source in order to point to an actual solution (someone else can write the spec, as I'm not even good at reading those), but in the meantime, I'm 100% sure a backwards-compatible programmatic solution exists.

trusktr commented May 31, 2016

@domenic That quote that you referred to makes me think exactly the same thing as I just wrote:

The necessary info for the HTML engine to upgrade that element properly is right there, in { extends: "button" }. I do believe there's some way to make this work (as there always is with software, because we make it, it is ours, we make software do what we want, and this is absolutely true without me having to read a single line of browser source code to know it), and that is="" is not required and can be completely removed from the spec because it seems like a hack to some limitation that can be solved somehow else (I don't know what that "somehow" is specifically, but I do know that the "somehow" exists because there's always some way to modify software to make it behave how we want within the limitations of current hardware where current hardware is not imposing any limitation on this feature).

You're pointing to something that exists in a spec, but I'm saying it doesn't have to be that way, specs can be modified. There is a solution (I don't know what that solution is specifically yet, but I know that the hardware on any computer that runs a browser nowadays isn't a limitation, and the only limitation is in the software we write, which can be modified). I simply can't understand why an HTML engine must need <button is="super-button"> (besides the fact that current algorithms require that) when clearly there's a JavaScript way to make the HTML engine understand that <super-button> means the same as <button is="super-button"> due to the fact we're defining that in the call to customElement.define.

I wish I had the time and resources to read the Chromium source in order to point to an actual solution (someone else can write the spec, as I'm not even good at reading those), but in the meantime, I'm 100% sure a backwards-compatible programmatic solution exists.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic May 31, 2016

Contributor

Yes, everything is Turing machines and everything can be implemented. However, there are real-world constraints---both in implementation complexity and in ecosystem impact---that make it not desirable.

Contributor

domenic commented May 31, 2016

Yes, everything is Turing machines and everything can be implemented. However, there are real-world constraints---both in implementation complexity and in ecosystem impact---that make it not desirable.

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr May 31, 2016

Some things are complex to implement, but this particular change doesn't seem to be. :]

Would you mind kindly pointing to the complexity and impact that make it non-desirable?

Is it because if the element isn't registered (i.e. with JS turned off) that the browser won't know to at least render a fallback <button>? I think that's perfectly fine, because without JS the app probably won't work anyways, and I'm willing to bet that most people who will write JavaScript-based Custom Elements won't care if the app doesn't work with JS turned off. The Nike website doesn't work with JS turned off, and they care. Most people won't care. Developers who do care can just as easily use <noscript> and place a <button> in there, which makes much more semantic sense anyway.

What other reason might there be?

trusktr commented May 31, 2016

Some things are complex to implement, but this particular change doesn't seem to be. :]

Would you mind kindly pointing to the complexity and impact that make it non-desirable?

Is it because if the element isn't registered (i.e. with JS turned off) that the browser won't know to at least render a fallback <button>? I think that's perfectly fine, because without JS the app probably won't work anyways, and I'm willing to bet that most people who will write JavaScript-based Custom Elements won't care if the app doesn't work with JS turned off. The Nike website doesn't work with JS turned off, and they care. Most people won't care. Developers who do care can just as easily use <noscript> and place a <button> in there, which makes much more semantic sense anyway.

What other reason might there be?

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr May 31, 2016

When someone who comes from React tries to learn Custom Elements for the first time (which is more likely to happen than someone learning Custom Elements first due to the mere fact that specs are hard to read compared to things like React docs, unfortunately, because specs are meant for implementers, and the actual usage docs if any are sometimes limited or hard to find), they'll face these weird things like the redundant is="" form of extension and globally-registered custom elements that are not registered on a per-component basis.

This is what classes are for, and in the case of the HTML engine where multiple types of elements may share the same interface (which is probably a partial cause to any current problems) the third argument to customElements.define is there to clarify things. The is="" attribute is simply redundant and out of place from an outter API perspective (which may matter more than whatever extra implementation complexity there may be) because we already specify the extension in the define() call.

I'd take this further and propose that every single built in element should have a single unique associated class, and therefore ES2015 class extension would be the only required form of extension definition.

Just in case, let me point out we must currently specify an extension three whole times. For example, to extend a button:

class MyButton extends HTMLButtonElement {} // 1
customElements.define('my-button', MyButton, { extends: 'button' }) // 2
<button is="my-button"></button> <!-- 3 -->

That's three times we've needed to specify that we're extending something, just to extend one thing. Doesn't this strike you as a bad idea from an end-user perspective?

It should ideally just be this, assuming that there is a one-to-one relationship between built-in elements and JS classes:

class MyButton extends HTMLButtonElement {} // 1
customElements.define('my-button', MyButton)
<my-button></my-button>

I get what you're saying about complexities and real-world world constraints, but I think "breaking the web" might actually fix the web in the long run. There can be an API cleansing, and websites may have to specify that the new breaking-API is to be used by supplying an HTTP header. Previous behavior can be supported for a matter of years and finally dropped, the new API becoming default at that time. Sites that don't update within a matter of years probably aren't cared for anyways.

I'm just arguing for a better web overall, and breaking changes are sometimes necessary. It seems I may be arguing for this forever though. Unless I am an employee at a browser vendor, I'm not sure my end-user viewpoints matter much. The least I can do is share them.

trusktr commented May 31, 2016

When someone who comes from React tries to learn Custom Elements for the first time (which is more likely to happen than someone learning Custom Elements first due to the mere fact that specs are hard to read compared to things like React docs, unfortunately, because specs are meant for implementers, and the actual usage docs if any are sometimes limited or hard to find), they'll face these weird things like the redundant is="" form of extension and globally-registered custom elements that are not registered on a per-component basis.

This is what classes are for, and in the case of the HTML engine where multiple types of elements may share the same interface (which is probably a partial cause to any current problems) the third argument to customElements.define is there to clarify things. The is="" attribute is simply redundant and out of place from an outter API perspective (which may matter more than whatever extra implementation complexity there may be) because we already specify the extension in the define() call.

I'd take this further and propose that every single built in element should have a single unique associated class, and therefore ES2015 class extension would be the only required form of extension definition.

Just in case, let me point out we must currently specify an extension three whole times. For example, to extend a button:

class MyButton extends HTMLButtonElement {} // 1
customElements.define('my-button', MyButton, { extends: 'button' }) // 2
<button is="my-button"></button> <!-- 3 -->

That's three times we've needed to specify that we're extending something, just to extend one thing. Doesn't this strike you as a bad idea from an end-user perspective?

It should ideally just be this, assuming that there is a one-to-one relationship between built-in elements and JS classes:

class MyButton extends HTMLButtonElement {} // 1
customElements.define('my-button', MyButton)
<my-button></my-button>

I get what you're saying about complexities and real-world world constraints, but I think "breaking the web" might actually fix the web in the long run. There can be an API cleansing, and websites may have to specify that the new breaking-API is to be used by supplying an HTTP header. Previous behavior can be supported for a matter of years and finally dropped, the new API becoming default at that time. Sites that don't update within a matter of years probably aren't cared for anyways.

I'm just arguing for a better web overall, and breaking changes are sometimes necessary. It seems I may be arguing for this forever though. Unless I am an employee at a browser vendor, I'm not sure my end-user viewpoints matter much. The least I can do is share them.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Jun 1, 2016

Contributor

The problem here is that UA internally checks local name e.g. element.localName == aTag to do various things, and all of that code needs to be updated to account for the fact there could be an intense of HTMLAnchorElement whose local name is not a.

Now I'm going to re-iterate that Apple objects to extending subclasses of HTMLElement using is= as currently spec'ed for various reasons we've stated in the past and this feature won't be supported in WebKit.

Contributor

rniwa commented Jun 1, 2016

The problem here is that UA internally checks local name e.g. element.localName == aTag to do various things, and all of that code needs to be updated to account for the fact there could be an intense of HTMLAnchorElement whose local name is not a.

Now I'm going to re-iterate that Apple objects to extending subclasses of HTMLElement using is= as currently spec'ed for various reasons we've stated in the past and this feature won't be supported in WebKit.

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jun 7, 2016

The problem here is that UA internally checks local name e.g. element.localName == aTag to do various things, and all of that code needs to be updated to account for the fact there could be an intense of HTMLAnchorElement whose local name is not a.

I think that relying on element tag names might be a bad idea, especially if we give users ability to scope elements on a per-shadow-root basis (now that's a good idea!). If we can allow end users of a custom element to assign any name they wish to that custom element used within the user's component (within the user's component's inner shadow tree), then we can avoid the global mess that window.customElements will be when an app gets large and when people widely adopt Web Components (wide adoption is one of the goals here).

In React (by contrast) the creator of a component never decides what the name of that component will be in the end user's inner tree. The end user has full control over the name of an imported component thanks to ES6 Modules. We can encourage a similar concept by introducing a per-shadow-root custom element registration feature.

If we can map names to classes without collision and without two names assigned to the same class (i.e. without elements like <q> and <blockquote> sharing the same class), then we should be able to use instanceof instead of checking tag names, and element inheritance can be based solely on ES6 class extension, as it should be (rather than having the extra is="" feature). The change that would be needed here would be to ensure that the native elements map to separate leaf classes so it's possible to tell a <q> element apart from a <blockquote> element with instanceof and therefore encouraging that sort of checking over tag names.

This API is still v0 in practice, so I think there's room for modification even if v1 is on the verge of coming out.

Apple objects to extending subclasses of HTMLElement using is=

I think Webkit not implementing is="" may be a great thing.

If we continue with globally registered elements, it will be inevitable that some two components in an app will have a name collision, and that the developer of that app will attempt to fix the problem by modifying the source of one custom element (possibly requiring forking of an NPM module, pushing to a new git repo, publishing a new module, etc, time consuming things) just to change the name of that element; it'd be a pain and can introduce unexpected errors if parts of the modified code are relying on the old tag names.

TLDR, if we put more care into the JavaScript aspects of an element (f.e. choosing which class represents an element, which is already happening in v1) then we'll have a more solid Web Component design, and we can drop is="". It will also require fixing things like the <q>/<blockquote> problem.

trusktr commented Jun 7, 2016

The problem here is that UA internally checks local name e.g. element.localName == aTag to do various things, and all of that code needs to be updated to account for the fact there could be an intense of HTMLAnchorElement whose local name is not a.

I think that relying on element tag names might be a bad idea, especially if we give users ability to scope elements on a per-shadow-root basis (now that's a good idea!). If we can allow end users of a custom element to assign any name they wish to that custom element used within the user's component (within the user's component's inner shadow tree), then we can avoid the global mess that window.customElements will be when an app gets large and when people widely adopt Web Components (wide adoption is one of the goals here).

In React (by contrast) the creator of a component never decides what the name of that component will be in the end user's inner tree. The end user has full control over the name of an imported component thanks to ES6 Modules. We can encourage a similar concept by introducing a per-shadow-root custom element registration feature.

If we can map names to classes without collision and without two names assigned to the same class (i.e. without elements like <q> and <blockquote> sharing the same class), then we should be able to use instanceof instead of checking tag names, and element inheritance can be based solely on ES6 class extension, as it should be (rather than having the extra is="" feature). The change that would be needed here would be to ensure that the native elements map to separate leaf classes so it's possible to tell a <q> element apart from a <blockquote> element with instanceof and therefore encouraging that sort of checking over tag names.

This API is still v0 in practice, so I think there's room for modification even if v1 is on the verge of coming out.

Apple objects to extending subclasses of HTMLElement using is=

I think Webkit not implementing is="" may be a great thing.

If we continue with globally registered elements, it will be inevitable that some two components in an app will have a name collision, and that the developer of that app will attempt to fix the problem by modifying the source of one custom element (possibly requiring forking of an NPM module, pushing to a new git repo, publishing a new module, etc, time consuming things) just to change the name of that element; it'd be a pain and can introduce unexpected errors if parts of the modified code are relying on the old tag names.

TLDR, if we put more care into the JavaScript aspects of an element (f.e. choosing which class represents an element, which is already happening in v1) then we'll have a more solid Web Component design, and we can drop is="". It will also require fixing things like the <q>/<blockquote> problem.

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jun 7, 2016

@domenic Can you describe the "ecosystem impact" that you might know of?

trusktr commented Jun 7, 2016

@domenic Can you describe the "ecosystem impact" that you might know of?

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Jun 7, 2016

Contributor

I am referring to the various tools which key on local name, including JavaScript frameworks, HTML preprocessors and postprocessors, HTML conformance checkers, and so on.

Contributor

domenic commented Jun 7, 2016

I am referring to the various tools which key on local name, including JavaScript frameworks, HTML preprocessors and postprocessors, HTML conformance checkers, and so on.

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jun 7, 2016

I think I know what you mean. For example, search engines read the tag names from the markup in order to detect what type of content and how to render it in the search results. This point may be moot because today it completely possible to use a root <app> component with a closed shadow root. In this case a search engine might only ever see

<html>
    <head>
        <title>foo</title>
    </head>
    <body>
        <app></app>
    </body>
</html>

That's functionally equivalent to an app with no shadow roots and made entirely of randomly named elements:

<html>
    <head>
        <title>foo</title>
    </head>
    <body>
        <asf>
            <oiur>
                ...
            </oiur>
            <urvcc>
                ...
            </urvcc>
        </asf>
    </body>
</html>

Based on that, I don't think it's necessary to keep element names.

I imagine a couple remedies:

  1. Allowing both forms and let users decide which to use: <button is="awesome-button"> or <awesome-button> and let users decide which to use as they so wish.
  2. Allowing overriding of native elements (which pairs well with the idea of per-shadow-root element registrations as in "hey, let me define what a <p> element is within my component that has no paragraphs."). Then <button> could behave like <awesome-button>, and search engines or other tools would be able to receive the same semantic meaning. This idea only works if the elements are in Light DOM.

trusktr commented Jun 7, 2016

I think I know what you mean. For example, search engines read the tag names from the markup in order to detect what type of content and how to render it in the search results. This point may be moot because today it completely possible to use a root <app> component with a closed shadow root. In this case a search engine might only ever see

<html>
    <head>
        <title>foo</title>
    </head>
    <body>
        <app></app>
    </body>
</html>

That's functionally equivalent to an app with no shadow roots and made entirely of randomly named elements:

<html>
    <head>
        <title>foo</title>
    </head>
    <body>
        <asf>
            <oiur>
                ...
            </oiur>
            <urvcc>
                ...
            </urvcc>
        </asf>
    </body>
</html>

Based on that, I don't think it's necessary to keep element names.

I imagine a couple remedies:

  1. Allowing both forms and let users decide which to use: <button is="awesome-button"> or <awesome-button> and let users decide which to use as they so wish.
  2. Allowing overriding of native elements (which pairs well with the idea of per-shadow-root element registrations as in "hey, let me define what a <p> element is within my component that has no paragraphs."). Then <button> could behave like <awesome-button>, and search engines or other tools would be able to receive the same semantic meaning. This idea only works if the elements are in Light DOM.
@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Jun 7, 2016

Contributor

Remedy 1 is what we have consensus on.

Contributor

domenic commented Jun 7, 2016

Remedy 1 is what we have consensus on.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Jun 7, 2016

Contributor

I'll note that we've vocally and repeatedly objected to having is=, and in fact, stated publicly that we won't implement this feature, but somehow the WG kept it in the spec. I wouldn't call that a consensus. We extremely reluctantly agreed to keep it in the spec until it gets removed at risk later.

Contributor

rniwa commented Jun 7, 2016

I'll note that we've vocally and repeatedly objected to having is=, and in fact, stated publicly that we won't implement this feature, but somehow the WG kept it in the spec. I wouldn't call that a consensus. We extremely reluctantly agreed to keep it in the spec until it gets removed at risk later.

@Zambonifofex

This comment has been minimized.

Show comment
Hide comment
@Zambonifofex

Zambonifofex Jun 10, 2016

It’s important to note that a lot of elements are implemented partially (or completely) with CSS. So, in order to support this and allow user agents to continue to implement elements like they do today, a new CSS pseudo-class would be needed: :subtypeof(button)

Zambonifofex commented Jun 10, 2016

It’s important to note that a lot of elements are implemented partially (or completely) with CSS. So, in order to support this and allow user agents to continue to implement elements like they do today, a new CSS pseudo-class would be needed: :subtypeof(button)

@chaals

This comment has been minimized.

Show comment
Hide comment
@chaals

chaals Jun 11, 2016

Contributor

a new CSS pseudo-class would be needed: :subtypeof(button)

Not really. The point of the is= approach is that since you have a button, that already matches CSS selectors for button. If you want to match a specific subtype you can do button[is=broken-button]

Contributor

chaals commented Jun 11, 2016

a new CSS pseudo-class would be needed: :subtypeof(button)

Not really. The point of the is= approach is that since you have a button, that already matches CSS selectors for button. If you want to match a specific subtype you can do button[is=broken-button]

@Zambonifofex

This comment has been minimized.

Show comment
Hide comment
@Zambonifofex

Zambonifofex Jun 11, 2016

@chaals right. You would need the pseudo-class if using a custom element that extends a native button could be done like <awesome-button> instead of <button is="awesome-button">.

Zambonifofex commented Jun 11, 2016

@chaals right. You would need the pseudo-class if using a custom element that extends a native button could be done like <awesome-button> instead of <button is="awesome-button">.

@Zambonifofex

This comment has been minimized.

Show comment
Hide comment
@Zambonifofex

Zambonifofex Jun 11, 2016

Also, like @trusktr said, if this and whatwg/html#896 become a thing, I think it’ll be possible to remove altogether the extends property from the options argument. We would then write customElements.define("awesome-button", class extends HTMLButtonElement{}, {}), which I really like.

Offering this as an alternative to the current way of defining extending native elements — allowing user agent developers and authors to choose which they want to use — is the best solution in my opinion. As @trusktr said, “I think ‘breaking the web’ might actually fix the web in the long run”.

Zambonifofex commented Jun 11, 2016

Also, like @trusktr said, if this and whatwg/html#896 become a thing, I think it’ll be possible to remove altogether the extends property from the options argument. We would then write customElements.define("awesome-button", class extends HTMLButtonElement{}, {}), which I really like.

Offering this as an alternative to the current way of defining extending native elements — allowing user agent developers and authors to choose which they want to use — is the best solution in my opinion. As @trusktr said, “I think ‘breaking the web’ might actually fix the web in the long run”.

@chaals

This comment has been minimized.

Show comment
Hide comment
@chaals

chaals Jun 11, 2016

Contributor

(@Zambonifofex it's generally not clear to me what you mean when you say "this", which makes it hard to engage)

As @trusktr said, “I think ‘breaking the web’ might actually fix the web in the long run”.

I've tried breaking the Web, at the turn of the century when it was smaller, to do something that would have been really useful. It failed. For pretty much the reasons given by people who say "you can't break the web".

Although user agent developers are less reluctant to break things than they were a decade ago, they're still very reluctant, and I believe with very good reason.

I think a strategy reliant on breaking the Web to fix it is generally a non-starter, even if the alternatives are far more painful.

Contributor

chaals commented Jun 11, 2016

(@Zambonifofex it's generally not clear to me what you mean when you say "this", which makes it hard to engage)

As @trusktr said, “I think ‘breaking the web’ might actually fix the web in the long run”.

I've tried breaking the Web, at the turn of the century when it was smaller, to do something that would have been really useful. It failed. For pretty much the reasons given by people who say "you can't break the web".

Although user agent developers are less reluctant to break things than they were a decade ago, they're still very reluctant, and I believe with very good reason.

I think a strategy reliant on breaking the Web to fix it is generally a non-starter, even if the alternatives are far more painful.

@chaals

This comment has been minimized.

Show comment
Hide comment
@chaals

chaals Jun 11, 2016

Contributor

For what it's worth, I agree with @rniwa's characterisation - we have an agreement that we won't write is= out right now, but I don't see a strong consensus behind it. (Although as an individual I'm one of those who think it's pretty much indispensable to making this stuff work).

Contributor

chaals commented Jun 11, 2016

For what it's worth, I agree with @rniwa's characterisation - we have an agreement that we won't write is= out right now, but I don't see a strong consensus behind it. (Although as an individual I'm one of those who think it's pretty much indispensable to making this stuff work).

@Zambonifofex

This comment has been minimized.

Show comment
Hide comment
@Zambonifofex

Zambonifofex Jun 12, 2016

@chaals

(@Zambonifofex it's generally not clear to me what you mean when you say "this", which makes it hard to engage)

When I say “this” I mean “this issue”; the suggestion made by @trusktr in the first comment of this thread.

But, honestly, — not that I’d be able to know, as I’ve never touched any browser’s code — I think that this would be a much easier change to make for user agent developers than you guys are making it out to be.

Zambonifofex commented Jun 12, 2016

@chaals

(@Zambonifofex it's generally not clear to me what you mean when you say "this", which makes it hard to engage)

When I say “this” I mean “this issue”; the suggestion made by @trusktr in the first comment of this thread.

But, honestly, — not that I’d be able to know, as I’ve never touched any browser’s code — I think that this would be a much easier change to make for user agent developers than you guys are making it out to be.

@Zambonifofex

This comment has been minimized.

Show comment
Hide comment
@Zambonifofex

Zambonifofex Jun 12, 2016

I mean, sure, it could be slightly annoying to go and change all of the checks for the tag name to something else, but would it be actually hard?

Zambonifofex commented Jun 12, 2016

I mean, sure, it could be slightly annoying to go and change all of the checks for the tag name to something else, but would it be actually hard?

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jul 2, 2016

The is="" API can be confusing. For example (in current v0 API):

<my-el></my-el>
class MyEl extends HTMLElement { /*... */ }
MyEl.extends = 'div'
document.registerElement('my-el', MyEl)

// ...

document.querySelector('my-el') instanceof MyEl // true or false?

trusktr commented Jul 2, 2016

The is="" API can be confusing. For example (in current v0 API):

<my-el></my-el>
class MyEl extends HTMLElement { /*... */ }
MyEl.extends = 'div'
document.registerElement('my-el', MyEl)

// ...

document.querySelector('my-el') instanceof MyEl // true or false?
@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Jul 2, 2016

Contributor

That API does not reflect the current custom elements API.

Contributor

domenic commented Jul 2, 2016

That API does not reflect the current custom elements API.

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jul 2, 2016

We should make the result of instanceof be obvious. I think this could be better in v1 (without is="" and without options.extends):

<my-el></my-el>
class MyEl extends HTMLDivElement { /*... */ }
customElements.define('my-el', MyEl)

// ...

document.querySelector('my-el') instanceof MyEl // true!

trusktr commented Jul 2, 2016

We should make the result of instanceof be obvious. I think this could be better in v1 (without is="" and without options.extends):

<my-el></my-el>
class MyEl extends HTMLDivElement { /*... */ }
customElements.define('my-el', MyEl)

// ...

document.querySelector('my-el') instanceof MyEl // true!
@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jul 2, 2016

I forgot this line too, in the previous comment:

document.querySelector('my-el') instanceof HTMLDivElement // also true!

trusktr commented Jul 2, 2016

I forgot this line too, in the previous comment:

document.querySelector('my-el') instanceof HTMLDivElement // also true!
@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jul 2, 2016

(@domenic I'm still using v0 in Chrome)

trusktr commented Jul 2, 2016

(@domenic I'm still using v0 in Chrome)

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jul 3, 2016

@rniwa

there could be an intense of HTMLAnchorElement whose local name is not a.

I think you meant "instance" instead of "intense"? This seems to be the reason why is="" exists, making it possible to create new elements that extend from elements that inherit from the same class, but that possibly have new behavior added to them in their definitions along with differing nodeNames. This is also the reason for the {extends: "element-name"} (I am calling it options.extends), which effectively accomplishes the same thing. I feel like this may be an anti-pattern considering that we now have ES6 classes for describing extension.

I am against both of those methods of defining extension, as I believe encouraging ES6 Classes as the the tool for defining extension will reduce room for confusion.

trusktr commented Jul 3, 2016

@rniwa

there could be an intense of HTMLAnchorElement whose local name is not a.

I think you meant "instance" instead of "intense"? This seems to be the reason why is="" exists, making it possible to create new elements that extend from elements that inherit from the same class, but that possibly have new behavior added to them in their definitions along with differing nodeNames. This is also the reason for the {extends: "element-name"} (I am calling it options.extends), which effectively accomplishes the same thing. I feel like this may be an anti-pattern considering that we now have ES6 classes for describing extension.

I am against both of those methods of defining extension, as I believe encouraging ES6 Classes as the the tool for defining extension will reduce room for confusion.

@trusktr trusktr changed the title from Why must the is="" attribute exist? to The is="" attribute is confusing? Maybe we should encourage only ES6 class-based extension. Jul 3, 2016

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jul 3, 2016

Perhaps existing native elements should be re-worked so that any differences they have should rather be described (explained) by using class extension, and not using those other two methods (is="" and options.extends). An example of native elements that are defined with the same interface/class but with different nodeNames are q and blockquote which share the HTMLQuoteElement interface. We should attempt to describe the difference (if any) between these elements with class extension rather than is="" combined with options.extends. If there is no difference except for nodeName, then if they are both instanceof the same class that is fine too as long as the behavior for both elements is entirely defined in the shared class, and not by other means.

trusktr commented Jul 3, 2016

Perhaps existing native elements should be re-worked so that any differences they have should rather be described (explained) by using class extension, and not using those other two methods (is="" and options.extends). An example of native elements that are defined with the same interface/class but with different nodeNames are q and blockquote which share the HTMLQuoteElement interface. We should attempt to describe the difference (if any) between these elements with class extension rather than is="" combined with options.extends. If there is no difference except for nodeName, then if they are both instanceof the same class that is fine too as long as the behavior for both elements is entirely defined in the shared class, and not by other means.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Jul 5, 2016

Contributor

FWIW, we're against subclassing subclasses of HTMLElement (e.g. HTMLInputElement, etc...) for various reasons, so WebKit isn't going to support this feature anyway. Extension of builtin elements are much better served with mixins.

Contributor

rniwa commented Jul 5, 2016

FWIW, we're against subclassing subclasses of HTMLElement (e.g. HTMLInputElement, etc...) for various reasons, so WebKit isn't going to support this feature anyway. Extension of builtin elements are much better served with mixins.

@Zambonifofex

This comment has been minimized.

Show comment
Hide comment
@Zambonifofex

Zambonifofex Jul 5, 2016

@rniwa if you could enumerate a couple of these “various reasons” or link to some previous discussion on this subject that lead to this conclusion, it’d be appreciated.

Zambonifofex commented Jul 5, 2016

@rniwa if you could enumerate a couple of these “various reasons” or link to some previous discussion on this subject that lead to this conclusion, it’d be appreciated.

@bedeoverend

This comment has been minimized.

Show comment
Hide comment
@bedeoverend

bedeoverend Jul 6, 2016

@rniwa I second @Zambonifofex, would love to know the rational behind this. In the case of needing features exclusive to native elements, how would someone use these features on a custom element without extending it?

An example my company is dealing with is replicating the sizing of a native img element, so far we haven't found any cross browser way to replicate the way it sizes, therefore having an element extension, so right now using is, is the only way to achieve this.

bedeoverend commented Jul 6, 2016

@rniwa I second @Zambonifofex, would love to know the rational behind this. In the case of needing features exclusive to native elements, how would someone use these features on a custom element without extending it?

An example my company is dealing with is replicating the sizing of a native img element, so far we haven't found any cross browser way to replicate the way it sizes, therefore having an element extension, so right now using is, is the only way to achieve this.

@Zambonifofex

This comment has been minimized.

Show comment
Hide comment
@Zambonifofex

Zambonifofex Jul 6, 2016

@bedeoverend I don’t think they are against extending native elements, they are simply against classes extending subclasses of HTMLElement. So you would still be able to extend native elements using the extends property of the customElements.define method. What would be illegal is the class passed to the method to extend a subclass of HTMLElement.

Zambonifofex commented Jul 6, 2016

@bedeoverend I don’t think they are against extending native elements, they are simply against classes extending subclasses of HTMLElement. So you would still be able to extend native elements using the extends property of the customElements.define method. What would be illegal is the class passed to the method to extend a subclass of HTMLElement.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Jul 6, 2016

Contributor

We won't be supporting extends either.

One fundamental problem is that subclassing a subclass of HTMLInputElement or HTMLImageElement often leads to a violation of the Liskov substitution principle over time. Because they're builtin elements, they're very likely to be extended in the future. Imagine that we had this feature supported before type=date was added. Then your subclass of HTMLInputElement must also support type=date in order to satisfy the substitution principle. Similarly, imagine we had added this feature before srcset was added to HTMLImageElement and you wrote a subclass of HTMLImageElement. Then not supporting srcset results in the violation of the principle again.

In addition, none of the builtin elements are designed to be subclassed, and in fact, we don't have builtin subclasses of HTMLElement that are also builtins. This limits the usefulness of any sort of subclassing. Furthermore, we don't have any hooks for the processing models and internal states of builtin elements such as the dirtiness of HTMLImageElement's value and when and how HTMLInputElement picks the appropriate image.

Finally, the whole design of is= is a hack, and it would harm the long term health of the Web platform.

Contributor

rniwa commented Jul 6, 2016

We won't be supporting extends either.

One fundamental problem is that subclassing a subclass of HTMLInputElement or HTMLImageElement often leads to a violation of the Liskov substitution principle over time. Because they're builtin elements, they're very likely to be extended in the future. Imagine that we had this feature supported before type=date was added. Then your subclass of HTMLInputElement must also support type=date in order to satisfy the substitution principle. Similarly, imagine we had added this feature before srcset was added to HTMLImageElement and you wrote a subclass of HTMLImageElement. Then not supporting srcset results in the violation of the principle again.

In addition, none of the builtin elements are designed to be subclassed, and in fact, we don't have builtin subclasses of HTMLElement that are also builtins. This limits the usefulness of any sort of subclassing. Furthermore, we don't have any hooks for the processing models and internal states of builtin elements such as the dirtiness of HTMLImageElement's value and when and how HTMLInputElement picks the appropriate image.

Finally, the whole design of is= is a hack, and it would harm the long term health of the Web platform.

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jul 6, 2016

FWIW, we're against subclassing subclasses of HTMLElement (e.g. HTMLInputElement, etc...) for various reasons, so WebKit isn't going to support this feature anyway. Extension of builtin elements are much better served with mixins.

One fundamental problem is that subclassing a subclass of HTMLInputElement or HTMLImageElement often leads to a violation of the Liskov substitution principle over time.

@rniwa True, which is why we should avoid subclassing when we can, but I think it's needed in order to fix the mistake that was made with q and blockquote. Otherwise, how does the web manifesto explain the difference between <q> and <blockquote>? I.e. How would the customElements.define() calls for q and blockquote differ in order to theoretically satisfy the web manifesto?

Maybe we would need to reserve HTMLQuoteElement for the q tag, and make a new HTMLBlockQuoteElement for the blockquote element, both of which extend from HTMLElement. I can see how that would be one way to satisfy the web manifesto and remain closer to Liskov's substitution principle.

trusktr commented Jul 6, 2016

FWIW, we're against subclassing subclasses of HTMLElement (e.g. HTMLInputElement, etc...) for various reasons, so WebKit isn't going to support this feature anyway. Extension of builtin elements are much better served with mixins.

One fundamental problem is that subclassing a subclass of HTMLInputElement or HTMLImageElement often leads to a violation of the Liskov substitution principle over time.

@rniwa True, which is why we should avoid subclassing when we can, but I think it's needed in order to fix the mistake that was made with q and blockquote. Otherwise, how does the web manifesto explain the difference between <q> and <blockquote>? I.e. How would the customElements.define() calls for q and blockquote differ in order to theoretically satisfy the web manifesto?

Maybe we would need to reserve HTMLQuoteElement for the q tag, and make a new HTMLBlockQuoteElement for the blockquote element, both of which extend from HTMLElement. I can see how that would be one way to satisfy the web manifesto and remain closer to Liskov's substitution principle.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Jul 6, 2016

Contributor

Yeah, there has been a discussion about that for q and blockquote. A more likely consensus is to introduce two subclasses of HTMLQuoteElement, e.g. HTMLInlineQuoteElement and HTMLBlockQuoteElement and have q and blockquote each use it. It's a bit more hairly story for h1 through h6, which seems to require an introduction of six subclasses of HTMLHeadingElement from HTMLH1Element through HTMLH6Element.

Contributor

rniwa commented Jul 6, 2016

Yeah, there has been a discussion about that for q and blockquote. A more likely consensus is to introduce two subclasses of HTMLQuoteElement, e.g. HTMLInlineQuoteElement and HTMLBlockQuoteElement and have q and blockquote each use it. It's a bit more hairly story for h1 through h6, which seems to require an introduction of six subclasses of HTMLHeadingElement from HTMLH1Element through HTMLH6Element.

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jul 6, 2016

I believe that would be totally fine along with agreement that subclassing in general is better avoided when possible. Nothing's perfect, but having those new subclasses for end-webdevs to extend from is already better than is=""+options.extends.

trusktr commented Jul 6, 2016

I believe that would be totally fine along with agreement that subclassing in general is better avoided when possible. Nothing's perfect, but having those new subclasses for end-webdevs to extend from is already better than is=""+options.extends.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Jul 6, 2016

Contributor

having those new subclasses for end-webdevs to extend from is already better than is=""+options.extends.

While I can agree is="" is not perfect---perhaps even "a hack", although IMO a pretty reasonable one---I think this opinion is just wrong. Web devs and users both benefit greatly from the ability to extend built-in elements, and is="" is the most practical solution so far. That is why the consensus was to keep is="" in the spec and let implementations decide its fate: because it allows developers to write less code and get more correct behavior, and because it helps users---especially disabled users---interact with web pages in a more predictable and easy manner.

You can talk about how you dislike the technical aspects of is="" as a solution, but I think it's very unfair to say that omitting is="" is better for webdevs (or even "end-webdevs", although I'm not sure what those are).

Contributor

domenic commented Jul 6, 2016

having those new subclasses for end-webdevs to extend from is already better than is=""+options.extends.

While I can agree is="" is not perfect---perhaps even "a hack", although IMO a pretty reasonable one---I think this opinion is just wrong. Web devs and users both benefit greatly from the ability to extend built-in elements, and is="" is the most practical solution so far. That is why the consensus was to keep is="" in the spec and let implementations decide its fate: because it allows developers to write less code and get more correct behavior, and because it helps users---especially disabled users---interact with web pages in a more predictable and easy manner.

You can talk about how you dislike the technical aspects of is="" as a solution, but I think it's very unfair to say that omitting is="" is better for webdevs (or even "end-webdevs", although I'm not sure what those are).

@ebidel

This comment has been minimized.

Show comment
Hide comment
@ebidel

ebidel Jul 6, 2016

I've built web components for many years and talk with a lot of web developers regularly about them. I'd like to look at this question from their perspective. Devs don't care about implementation challenges or diving into the weeds.

We need to keep customized built-in elements. CE v1 is not a spec without a way to extend existing HTML elements. My talk at the Progressive Web App Summit highlights some of the reasons why it's important to keep:

  • is="" is 100% progressive enhancement!
  • Utilize built-in a11y features of the element. This is one of the primary reasons to keep it. I've seen far too many devs build custom elements that miss out on basic a11y...forget it altogether 👎.
  • More DRY. One gains native features for free (keyboard behavior, imperative API, and styling). Even recreating all the benefits of <button> requires way too much code.
  • Autonomous elements don't have the black magic that native elements have (e.g. creating an element that participates in <form> is painful). Same with extending other types of children.
  • Existing tools understand built-in elements
  • Existing CSS theming libraries work better with it
  • In Polymer, folks really love the extended helpers (<template is="dom-repeat">, <template is="dom-if">,`. They're easy to reason about, declarative, and simple to use. You know exactly what you're getting.
  • I talk to a lot of web developers. They don't mind is="".
  • No other component library gives you all ^ for free.

Whether is="" is the way to extend elements or not doesn't matter to much. But we definitely need a solution rather than no solution and currently, that's is="". Personally, I see nothing wrong with is="". It's worked great for the last couple of years in Chrome's v0 implementation. It's been implemented in a browser and proven. There may be edge cases with subclassing certain elements, but TBH, I haven't seen many (any?) developers run into them.

Ultimately, developers have to write less code with is="". That's huge. Extensions bring more reuse and keep best practices to our platform. Not less. Let's not remove such a useful feature.

ebidel commented Jul 6, 2016

I've built web components for many years and talk with a lot of web developers regularly about them. I'd like to look at this question from their perspective. Devs don't care about implementation challenges or diving into the weeds.

We need to keep customized built-in elements. CE v1 is not a spec without a way to extend existing HTML elements. My talk at the Progressive Web App Summit highlights some of the reasons why it's important to keep:

  • is="" is 100% progressive enhancement!
  • Utilize built-in a11y features of the element. This is one of the primary reasons to keep it. I've seen far too many devs build custom elements that miss out on basic a11y...forget it altogether 👎.
  • More DRY. One gains native features for free (keyboard behavior, imperative API, and styling). Even recreating all the benefits of <button> requires way too much code.
  • Autonomous elements don't have the black magic that native elements have (e.g. creating an element that participates in <form> is painful). Same with extending other types of children.
  • Existing tools understand built-in elements
  • Existing CSS theming libraries work better with it
  • In Polymer, folks really love the extended helpers (<template is="dom-repeat">, <template is="dom-if">,`. They're easy to reason about, declarative, and simple to use. You know exactly what you're getting.
  • I talk to a lot of web developers. They don't mind is="".
  • No other component library gives you all ^ for free.

Whether is="" is the way to extend elements or not doesn't matter to much. But we definitely need a solution rather than no solution and currently, that's is="". Personally, I see nothing wrong with is="". It's worked great for the last couple of years in Chrome's v0 implementation. It's been implemented in a browser and proven. There may be edge cases with subclassing certain elements, but TBH, I haven't seen many (any?) developers run into them.

Ultimately, developers have to write less code with is="". That's huge. Extensions bring more reuse and keep best practices to our platform. Not less. Let's not remove such a useful feature.

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Sep 7, 2017

@WebReflection

<any-element mixins="behavior1 behavior2" />

I think I prefer

<any-element components="behavior1 behavior2" />

or

<any-element behaviors="behavior1 behavior2" />

or

<any-element has="behavior1 behavior2" />

because these aren't exactly mixins as I described in my last comment, although there's room for conflict when/if multiple components/behaviors modify the same properties on the target element, which makes them only similar to mixins.

it would put an end to the is confusion and story, it can be easily polyfilled too.

Yeah! And more robust and flexible too, because behaviors could be added or removed independently of the original element. f.e., If the app starts with

<any-element has="behavior1 behavior2" />

then at any point later in runtime, some other developer could augment the element with further behavior as needed, and convert it to

<any-element has="behavior1 behavior2 behavior3" />

trusktr commented Sep 7, 2017

@WebReflection

<any-element mixins="behavior1 behavior2" />

I think I prefer

<any-element components="behavior1 behavior2" />

or

<any-element behaviors="behavior1 behavior2" />

or

<any-element has="behavior1 behavior2" />

because these aren't exactly mixins as I described in my last comment, although there's room for conflict when/if multiple components/behaviors modify the same properties on the target element, which makes them only similar to mixins.

it would put an end to the is confusion and story, it can be easily polyfilled too.

Yeah! And more robust and flexible too, because behaviors could be added or removed independently of the original element. f.e., If the app starts with

<any-element has="behavior1 behavior2" />

then at any point later in runtime, some other developer could augment the element with further behavior as needed, and convert it to

<any-element has="behavior1 behavior2 behavior3" />
@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Sep 7, 2017

On the idea of "behaviors", please let's continue in #662 rather than here. I've renamed the title to be more generic to show the idea is similar to is="" but should not necessarily named the same.

trusktr commented Sep 7, 2017

On the idea of "behaviors", please let's continue in #662 rather than here. I've renamed the title to be more generic to show the idea is similar to is="" but should not necessarily named the same.

@treshugart

This comment has been minimized.

Show comment
Hide comment
@treshugart

treshugart Sep 7, 2017

It might be worth looking at custom attributes. @matthewp has written something that might be a good basis for discussion: https://github.com/matthewp/custom-attributes. It would fit the custom element model quite well.

treshugart commented Sep 7, 2017

It might be worth looking at custom attributes. @matthewp has written something that might be a good basis for discussion: https://github.com/matthewp/custom-attributes. It would fit the custom element model quite well.

@treshugart

This comment has been minimized.

Show comment
Hide comment
@treshugart

treshugart Sep 8, 2017

Woops, I'll put that in #662. I had both open and commented in the wrong one...

treshugart commented Sep 8, 2017

Woops, I'll put that in #662. I had both open and commented in the wrong one...

@joshbruce

This comment has been minimized.

Show comment
Hide comment
@joshbruce

joshbruce Dec 3, 2017

I'll throw in, I think I read enough to get the gist. And, sorry for not reading the participation rules first (if there are any).

For this topic I want to say, I was thrilled to hear about is...and it had nothing to do with web-components as they relate to the Shadow DOM, HTML imports, and so on.

I think part of the issue is determining who the heavy in the room is, so to speak. Putting more of the definition in JS makes JS more of the heavy; thereby, tightly coupling markup to JS. (Given where we are, makes sense.)

Having said that, when I built my component libraries for PHP (obviously no JS there), here's where it's at (it looks a bit like React's createElement method, and when we use the factory it feels more like Elm's HTML library).

Component::make(string $element, array $attributes, Component ...$content)

Component::element(content, content, content, ...)->attr()

These factory methods return an instance of Component, not a subclass of it, yeah? There's a method you can call to "extend" it...change the HTML output (makes HTML the "heavy") - via the open to extension not modification principle.

So:

Component::my_button()->print()
// output
<my-button></my-button>
Component::my_button()->extends('button')->print()
// output
<button is="my-button"></my-button>

From an OO perspective, it's communicated literally like inheritance, but feels more like dependency injection:

Like my-button saying, "I am my own button (instance), but I depend on your button; so, come with me."

Flipping it around, again via open to extension, it's more like HTML's button saying, "I know you're your own button, but I'll take the lead here."

What I like about is as a term and for this use case is I take the definition and modifiers of the element out of class...that may seem silly, but I really hate writing:

// Class attribute as element declaration and aesthetic property junk drawer
<button class="my-button--link purple bordered"></button>

// Compared to the desired
<button is="my-button" class="link purple bordered"></button>

// And I can't write this because of the understandable one id per page constraint
<button id="my-button" class="link purple bordered"></button>

// And this is just painful and inaccurate semantically
<button data-is="my-button" class="link purple bordered"></button>

// And this opens up all sorts interesting possibilities for atomic design
<button is="my-button" id="unique-to-page" class="link purple bordered"></button>

Further, is solves the problem without complicating the API of an HTML element itself (lots of additional global properties added to the spec), which could happen if we don't establish a way of determining when something outgrows where it can currently live (class or data-*). (Trying to get HTML to be fully OO in a way with interfaces, inheritance, and traits...might be cool, but might be one of those instances of just because we can).

For things like implements or has, I'm thinking data-* might be more appropriate?

Why is is different for me and I prefer it over doing something like data-is="my-button"...is does not describe a property or properties the component has, it is making a declaration of what the component is. That's why, without much emphasis on or consideration for the JS side of this (much love and respect though), what we're trying to do feels less like object inheritance from OO and more like dependency injection...in either direction.

Even if nothing else comes of web components as it relates to custom elements, importable html + JS + CSS, inheritable elements...the concept of an is attribute is still viable with today's web technology (I think) and performs well semantically (though, I'd probably frown on "multiple inheritance" being able to add multiple values to is, because we already have and use class for that and I don't see a gain at least from a primarily HTML + CSS perspective).

  • From a purely HTML perspective, I'm finding this to be a very effective way to communicate what's going on in the markup to other developers. (Don't need to know what's happening in CSS or JS. Again, HTML is the heavy in the room.)
  • From a CSS/Sass perspective, I'm finding this to be a good way to reduce how much CSS is needed in general. button becomes a very generic definition. my-button is painless to select using the attribute selector. And class is cleaned out of everything except modifier utility things. It can become more modular like, where there are fewer declarations with multiple defined styles, and more defined styles with multiples declarations if that makes sense.
  • From a JS perspective (maybe not virtual DOM), I can instantiate all sorts of variations quickly and then let them die just as quickly (taking a more functional approach). Constant event observation, but not constant in-memory DOM existence. (Might be heresy to say that, unless you're still rocking old-school progressive enhancement.)

is might be a hack from a browser implementation and class instanciation perspective; however, from a pure UI definition HTML perspective an CSS perspective, it's pretty nice, I think.

Just throwing copper. Thanks.

joshbruce commented Dec 3, 2017

I'll throw in, I think I read enough to get the gist. And, sorry for not reading the participation rules first (if there are any).

For this topic I want to say, I was thrilled to hear about is...and it had nothing to do with web-components as they relate to the Shadow DOM, HTML imports, and so on.

I think part of the issue is determining who the heavy in the room is, so to speak. Putting more of the definition in JS makes JS more of the heavy; thereby, tightly coupling markup to JS. (Given where we are, makes sense.)

Having said that, when I built my component libraries for PHP (obviously no JS there), here's where it's at (it looks a bit like React's createElement method, and when we use the factory it feels more like Elm's HTML library).

Component::make(string $element, array $attributes, Component ...$content)

Component::element(content, content, content, ...)->attr()

These factory methods return an instance of Component, not a subclass of it, yeah? There's a method you can call to "extend" it...change the HTML output (makes HTML the "heavy") - via the open to extension not modification principle.

So:

Component::my_button()->print()
// output
<my-button></my-button>
Component::my_button()->extends('button')->print()
// output
<button is="my-button"></my-button>

From an OO perspective, it's communicated literally like inheritance, but feels more like dependency injection:

Like my-button saying, "I am my own button (instance), but I depend on your button; so, come with me."

Flipping it around, again via open to extension, it's more like HTML's button saying, "I know you're your own button, but I'll take the lead here."

What I like about is as a term and for this use case is I take the definition and modifiers of the element out of class...that may seem silly, but I really hate writing:

// Class attribute as element declaration and aesthetic property junk drawer
<button class="my-button--link purple bordered"></button>

// Compared to the desired
<button is="my-button" class="link purple bordered"></button>

// And I can't write this because of the understandable one id per page constraint
<button id="my-button" class="link purple bordered"></button>

// And this is just painful and inaccurate semantically
<button data-is="my-button" class="link purple bordered"></button>

// And this opens up all sorts interesting possibilities for atomic design
<button is="my-button" id="unique-to-page" class="link purple bordered"></button>

Further, is solves the problem without complicating the API of an HTML element itself (lots of additional global properties added to the spec), which could happen if we don't establish a way of determining when something outgrows where it can currently live (class or data-*). (Trying to get HTML to be fully OO in a way with interfaces, inheritance, and traits...might be cool, but might be one of those instances of just because we can).

For things like implements or has, I'm thinking data-* might be more appropriate?

Why is is different for me and I prefer it over doing something like data-is="my-button"...is does not describe a property or properties the component has, it is making a declaration of what the component is. That's why, without much emphasis on or consideration for the JS side of this (much love and respect though), what we're trying to do feels less like object inheritance from OO and more like dependency injection...in either direction.

Even if nothing else comes of web components as it relates to custom elements, importable html + JS + CSS, inheritable elements...the concept of an is attribute is still viable with today's web technology (I think) and performs well semantically (though, I'd probably frown on "multiple inheritance" being able to add multiple values to is, because we already have and use class for that and I don't see a gain at least from a primarily HTML + CSS perspective).

  • From a purely HTML perspective, I'm finding this to be a very effective way to communicate what's going on in the markup to other developers. (Don't need to know what's happening in CSS or JS. Again, HTML is the heavy in the room.)
  • From a CSS/Sass perspective, I'm finding this to be a good way to reduce how much CSS is needed in general. button becomes a very generic definition. my-button is painless to select using the attribute selector. And class is cleaned out of everything except modifier utility things. It can become more modular like, where there are fewer declarations with multiple defined styles, and more defined styles with multiples declarations if that makes sense.
  • From a JS perspective (maybe not virtual DOM), I can instantiate all sorts of variations quickly and then let them die just as quickly (taking a more functional approach). Constant event observation, but not constant in-memory DOM existence. (Might be heresy to say that, unless you're still rocking old-school progressive enhancement.)

is might be a hack from a browser implementation and class instanciation perspective; however, from a pure UI definition HTML perspective an CSS perspective, it's pretty nice, I think.

Just throwing copper. Thanks.

@joshbruce

This comment has been minimized.

Show comment
Hide comment
@joshbruce

joshbruce Dec 3, 2017

ps. When I wrote the HTML library, I had to reverse the "inheritance", and that's when "is" came into being.

Html::button()->is('my-button')

// Turns into
Component::my_button()->extends('button')

The third library in the set generates what I classify as my components. Those components could inherit directly from Component but, because I want to take advantage of the attribute ordering and content model validation of the Html library, I had them inherit directly from HtmlElement (as if they were an HTML element). But, the instances end up being throwaways, so to speak. Because all they do is instantiate and return things from Html and Component.

joshbruce commented Dec 3, 2017

ps. When I wrote the HTML library, I had to reverse the "inheritance", and that's when "is" came into being.

Html::button()->is('my-button')

// Turns into
Component::my_button()->extends('button')

The third library in the set generates what I classify as my components. Those components could inherit directly from Component but, because I want to take advantage of the attribute ordering and content model validation of the Html library, I had them inherit directly from HtmlElement (as if they were an HTML element). But, the instances end up being throwaways, so to speak. Because all they do is instantiate and return things from Html and Component.

@joshbruce

This comment has been minimized.

Show comment
Hide comment
@joshbruce

joshbruce Dec 3, 2017

pps. And it doesn't collide with something like.

<input is="my-email-input" id="unique" class="bordered" type="email"></input>

joshbruce commented Dec 3, 2017

pps. And it doesn't collide with something like.

<input is="my-email-input" id="unique" class="bordered" type="email"></input>
@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Feb 10, 2018

@joshbruce Seems like you like is="" more than has="". I'm curious to know more on that. Mind commenting on that over at #727, where I've published an implementation of element-behaviors and has=""?

trusktr commented Feb 10, 2018

@joshbruce Seems like you like is="" more than has="". I'm curious to know more on that. Mind commenting on that over at #727, where I've published an implementation of element-behaviors and has=""?

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Feb 10, 2018

@rniwa

FWIW, we're against subclassing subclasses of HTMLElement (e.g. HTMLInputElement, etc...) for various reasons, so WebKit isn't going to support this feature anyway. Extension of builtin elements are much better served with mixins.

I think subclassing if fine, but just that the {extends:___} arg for customElements.define and is="" are the wrong tool for it, based on why they are confusing in my OP. But, I still believe subclassing can generally be useful in various cases, and that it should just be as easy as normal inheritance.

To summarize, it'd be better to define inheritance with one step (rather than the current 3 steps), and I'll update the OP to make this more clear up front:

Current way, inheritance has to be specified three times:

class MyButton extends HTMLButtonElement {} // 1st time
customElements.define('my-button', MyButton, { extends: 'button' }) // 2nd time
<button is="my-button"></button> <!-- 3rd time -->

better way, inheritance specified once, much easier:

class MyButton extends HTMLButtonElement {} // 1st and only time
customElements.define('my-button', MyButton)
<my-button></my-button>

mixins

But @rniwa, you mentioned mixins, which I think are a new topic that can solve many needs completely aside from inheritance (which otherwise is="" here would solve). Do you mind commenting on #727 about what you like/dislike of my "element behaviors" and has="" attribute implementation, and we can go from there?

trusktr commented Feb 10, 2018

@rniwa

FWIW, we're against subclassing subclasses of HTMLElement (e.g. HTMLInputElement, etc...) for various reasons, so WebKit isn't going to support this feature anyway. Extension of builtin elements are much better served with mixins.

I think subclassing if fine, but just that the {extends:___} arg for customElements.define and is="" are the wrong tool for it, based on why they are confusing in my OP. But, I still believe subclassing can generally be useful in various cases, and that it should just be as easy as normal inheritance.

To summarize, it'd be better to define inheritance with one step (rather than the current 3 steps), and I'll update the OP to make this more clear up front:

Current way, inheritance has to be specified three times:

class MyButton extends HTMLButtonElement {} // 1st time
customElements.define('my-button', MyButton, { extends: 'button' }) // 2nd time
<button is="my-button"></button> <!-- 3rd time -->

better way, inheritance specified once, much easier:

class MyButton extends HTMLButtonElement {} // 1st and only time
customElements.define('my-button', MyButton)
<my-button></my-button>

mixins

But @rniwa, you mentioned mixins, which I think are a new topic that can solve many needs completely aside from inheritance (which otherwise is="" here would solve). Do you mind commenting on #727 about what you like/dislike of my "element behaviors" and has="" attribute implementation, and we can go from there?

@OvermindDL1

This comment has been minimized.

Show comment
Hide comment
@OvermindDL1

OvermindDL1 Feb 12, 2018

better way, inheritance specified once, much easier:

Then you reintroduce the problem of not being able to subclass tr/td/etc/and_a_few_others...

OvermindDL1 commented Feb 12, 2018

better way, inheritance specified once, much easier:

Then you reintroduce the problem of not being able to subclass tr/td/etc/and_a_few_others...

@wessberg

This comment has been minimized.

Show comment
Hide comment
@wessberg

wessberg Mar 13, 2018

As @OvermindDL1 touches on, the subclassing approach will not be functionally equivalent to the current approach with is because multiple built-in elements implement the same interface.

This could be solved by introducing a one-to-one relationship between HTML interfaces and element selectors. Alternatively, the approach could work if allowing the selector for the customized built-in element to be referenced within an is attribute for all built-in elements that implements that interface.

However, the subclassing approach would work great for new autonomous custom elements deriving from, say, HTMLButtonElement, working as in the current spec but allowing inheriting from other interfaces than HTMLElement, but I don’t see why customized built-in elements need to derive from HTMLElement or any of its descendents at all. Rather, they could be agnostic and receive a reference to the constructed element and otherwise support the regular custom element lifecycle hooks. So far, the proposal I’ve been most fond of has been Custom Attributes.

wessberg commented Mar 13, 2018

As @OvermindDL1 touches on, the subclassing approach will not be functionally equivalent to the current approach with is because multiple built-in elements implement the same interface.

This could be solved by introducing a one-to-one relationship between HTML interfaces and element selectors. Alternatively, the approach could work if allowing the selector for the customized built-in element to be referenced within an is attribute for all built-in elements that implements that interface.

However, the subclassing approach would work great for new autonomous custom elements deriving from, say, HTMLButtonElement, working as in the current spec but allowing inheriting from other interfaces than HTMLElement, but I don’t see why customized built-in elements need to derive from HTMLElement or any of its descendents at all. Rather, they could be agnostic and receive a reference to the constructed element and otherwise support the regular custom element lifecycle hooks. So far, the proposal I’ve been most fond of has been Custom Attributes.

@WebReflection

This comment has been minimized.

Show comment
Hide comment
@WebReflection

WebReflection May 31, 2018

Since Chrome 67 shipped Custom Elements with Builtins extend capability, and since Firefox is planning to release those as well, I have updated my polyfill and its test page to welcome native implementations to test against that page.

If your implementation passes all tests, the window.customElements object will be the native one, instead of the patched one.

This is already true for Chrome 67, I hope it'll be true for Firefox too once shipped.

Fun fact

The ce.getAttribute('is') returns always null even if ce.outerHTML would expose the is="custom-element" information. Not sure that was a bug, but it's easy to mess up with feature detection if you don't know that.

Thanks to the Chrome team for going out in a pragmatic way fulfilling the Custom Elements specification as a whole 🎉

WebReflection commented May 31, 2018

Since Chrome 67 shipped Custom Elements with Builtins extend capability, and since Firefox is planning to release those as well, I have updated my polyfill and its test page to welcome native implementations to test against that page.

If your implementation passes all tests, the window.customElements object will be the native one, instead of the patched one.

This is already true for Chrome 67, I hope it'll be true for Firefox too once shipped.

Fun fact

The ce.getAttribute('is') returns always null even if ce.outerHTML would expose the is="custom-element" information. Not sure that was a bug, but it's easy to mess up with feature detection if you don't know that.

Thanks to the Chrome team for going out in a pragmatic way fulfilling the Custom Elements specification as a whole 🎉

@garajo

This comment has been minimized.

Show comment
Hide comment
@garajo

garajo Jun 25, 2018

This is a great thread.

As a developer who is just considering using Web Components, I just wanted to add a point of view that I think has not been made yet. I'm not sure; I had to skip some parts of this thread.

Most of the discussion centers around the reuse of components by JavaScript developers. Is there any discussion yet on how Web Components would affect onboarding of potential HTML/Markup authors, specifically the effects in documentation and ease of adoption? It seems to me <table is="sortable" /> would be easier to adopt, and better leverage existing docs resources for those new to web development or those that would only work with Markup syntax, such as content authors. If I were to build a component for this audience, I could more bridge understanding and would save a lot of work referring to existing docs when onboarding potential HTML/Markup authors.

I'm not against extends, inheritance, nor mixins, etc. I'm happy to be educated and hear all contrary points. Please don't answer using metaphors.

garajo commented Jun 25, 2018

This is a great thread.

As a developer who is just considering using Web Components, I just wanted to add a point of view that I think has not been made yet. I'm not sure; I had to skip some parts of this thread.

Most of the discussion centers around the reuse of components by JavaScript developers. Is there any discussion yet on how Web Components would affect onboarding of potential HTML/Markup authors, specifically the effects in documentation and ease of adoption? It seems to me <table is="sortable" /> would be easier to adopt, and better leverage existing docs resources for those new to web development or those that would only work with Markup syntax, such as content authors. If I were to build a component for this audience, I could more bridge understanding and would save a lot of work referring to existing docs when onboarding potential HTML/Markup authors.

I'm not against extends, inheritance, nor mixins, etc. I'm happy to be educated and hear all contrary points. Please don't answer using metaphors.

@joshbruce

This comment has been minimized.

Show comment
Hide comment
@joshbruce

joshbruce Jun 25, 2018

@garajo: The comment I made (#509 (comment)) and the follow on discussion @trusktr I think touches on what you're talking about (not just for JS).

joshbruce commented Jun 25, 2018

@garajo: The comment I made (#509 (comment)) and the follow on discussion @trusktr I think touches on what you're talking about (not just for JS).

@WebReflection

This comment has been minimized.

Show comment
Hide comment
@WebReflection

WebReflection Jun 25, 2018

@garajo, @joshbruce, in case you have missed my reply, the is="c-e" attribute already landed, it works without flags in Chrome: it shipped 🎉

WebReflection commented Jun 25, 2018

@garajo, @joshbruce, in case you have missed my reply, the is="c-e" attribute already landed, it works without flags in Chrome: it shipped 🎉

@WebReflection

This comment has been minimized.

Show comment
Hide comment
@WebReflection

WebReflection Jul 2, 2018

WebReflection commented Jul 2, 2018

@oleersoy

This comment has been minimized.

Show comment
Hide comment
@oleersoy

oleersoy Jul 3, 2018

In that case make sure you create two CSS selectors. One for fancy-input and one for input. Make sure you add that to your project budget as well, and consider future modification costs will be as you will have twice as much code to refactor.

This type of stuff is missed by specifications all the time. Take default exports for example. Everyone who has used default exports has now made their code much more difficult to refactor and maintain. Careful what you wish for.

oleersoy commented Jul 3, 2018

In that case make sure you create two CSS selectors. One for fancy-input and one for input. Make sure you add that to your project budget as well, and consider future modification costs will be as you will have twice as much code to refactor.

This type of stuff is missed by specifications all the time. Take default exports for example. Everyone who has used default exports has now made their code much more difficult to refactor and maintain. Careful what you wish for.

@WebReflection

This comment has been minimized.

Show comment
Hide comment
@WebReflection

WebReflection Jul 3, 2018

In that case make sure you create two CSS selectors

nothing different from a class name though, so if the argument is input.fancy-input VS input[is=fancy-input] then I say there are really no arguments anymore against the is if not those made up for the sake of it.

WebReflection commented Jul 3, 2018

In that case make sure you create two CSS selectors

nothing different from a class name though, so if the argument is input.fancy-input VS input[is=fancy-input] then I say there are really no arguments anymore against the is if not those made up for the sake of it.

@WebReflection

This comment has been minimized.

Show comment
Hide comment
@WebReflection

WebReflection Jul 3, 2018

P.S. in case you missed it: custom elements builtins already shipped in Chrome and work as specifications say.

WebReflection commented Jul 3, 2018

P.S. in case you missed it: custom elements builtins already shipped in Chrome and work as specifications say.

@oleersoy

This comment has been minimized.

Show comment
Hide comment
@oleersoy

oleersoy Jul 3, 2018

So did default exports and I'm sure it has already cost our industry billions of dollars in refactoring and product complexity.

  1. Specification completes
  2. It's implemented
  3. It's built into tons of solutions
  4. We realize it's a really bad idea
  5. We start to refactor all the code that implemented the bad idea
  6. We teach people not to do it (Which adds to cost)

oleersoy commented Jul 3, 2018

So did default exports and I'm sure it has already cost our industry billions of dollars in refactoring and product complexity.

  1. Specification completes
  2. It's implemented
  3. It's built into tons of solutions
  4. We realize it's a really bad idea
  5. We start to refactor all the code that implemented the bad idea
  6. We teach people not to do it (Which adds to cost)
@WebReflection

This comment has been minimized.

Show comment
Hide comment
@WebReflection

WebReflection Jul 3, 2018

@oleersoy in this case you have quite a different situation:

  1. custom builtins were already possible in V0
  2. libraries shipping them already made everyone happy and are successful stories (Polymer, AFrame, others)
  3. specifications in V1 also defined them
  4. Chrome already shipped is="..." and AFAIK Firefox is following
  5. polyfills implemented that already and are successful stories too (Polymer, AFrame)
  6. you are really free to not use is="..." in all cases where it's not mandatory (not LI, TD, SELECT, OPTION, etcetera)

Custom Elements builtins are needed on the Web and indeed as soon as available tons of developers used those and it worked, as ugly as it looked.

So many things on the Web work, as ugly as these look, and new API that get replaced are also regular part of the Software history. In this case nobody came up with anything implementable in the next 5 years so you gotta be pragmatic and move forward, IMO.

WebReflection commented Jul 3, 2018

@oleersoy in this case you have quite a different situation:

  1. custom builtins were already possible in V0
  2. libraries shipping them already made everyone happy and are successful stories (Polymer, AFrame, others)
  3. specifications in V1 also defined them
  4. Chrome already shipped is="..." and AFAIK Firefox is following
  5. polyfills implemented that already and are successful stories too (Polymer, AFrame)
  6. you are really free to not use is="..." in all cases where it's not mandatory (not LI, TD, SELECT, OPTION, etcetera)

Custom Elements builtins are needed on the Web and indeed as soon as available tons of developers used those and it worked, as ugly as it looked.

So many things on the Web work, as ugly as these look, and new API that get replaced are also regular part of the Software history. In this case nobody came up with anything implementable in the next 5 years so you gotta be pragmatic and move forward, IMO.

@oleersoy

This comment has been minimized.

Show comment
Hide comment
@oleersoy

oleersoy Jul 3, 2018

It's not different. People who started using default exports thought it was a good idea. It made it into the specification and then tons of people used it and thought they were doing a great job. Now we see that it hurts our ability to refactor code and limits tree shaking, thus every product produced with default exports is less maintainable and heavier in terms of bandwidth than it needs to be.

The is attribute has the same effect. Everyone that uses it has to start making special considerations for their css.

  1. Is it a input[is=fancy-input] selector or
  2. a fancy-input selector in the event that the custom element was created by the custom element API?

The core reason we are using is is that we are dealing with user clients that may or may not support the custom element API. All UX / Experience designers will tell you to keep the user experience as consistent as possible across devices / platforms. Using the is attribute with this goal in mind has to add complexity in terms of markup, css, and API usage.

oleersoy commented Jul 3, 2018

It's not different. People who started using default exports thought it was a good idea. It made it into the specification and then tons of people used it and thought they were doing a great job. Now we see that it hurts our ability to refactor code and limits tree shaking, thus every product produced with default exports is less maintainable and heavier in terms of bandwidth than it needs to be.

The is attribute has the same effect. Everyone that uses it has to start making special considerations for their css.

  1. Is it a input[is=fancy-input] selector or
  2. a fancy-input selector in the event that the custom element was created by the custom element API?

The core reason we are using is is that we are dealing with user clients that may or may not support the custom element API. All UX / Experience designers will tell you to keep the user experience as consistent as possible across devices / platforms. Using the is attribute with this goal in mind has to add complexity in terms of markup, css, and API usage.

@WebReflection

This comment has been minimized.

Show comment
Hide comment
@WebReflection

WebReflection Jul 3, 2018

@oleersoy nobody gets hurt ever since the feature has been successfully polyfilled already for the last 4+ years. You are free to not use custom elements builtins, others are free to use those when needed.

I have also fully lost interest into this discussion because the feature already shipped and I want to be able to use it (as I've been for the last 4+ years anyway).

Best Regards

WebReflection commented Jul 3, 2018

@oleersoy nobody gets hurt ever since the feature has been successfully polyfilled already for the last 4+ years. You are free to not use custom elements builtins, others are free to use those when needed.

I have also fully lost interest into this discussion because the feature already shipped and I want to be able to use it (as I've been for the last 4+ years anyway).

Best Regards

@WebReflection

This comment has been minimized.

Show comment
Hide comment
@WebReflection

WebReflection Jul 3, 2018

Last thoughts:

The core reason we are using is is that we are dealing with user clients that may or may not support the custom element API. All UX / Experience designers will tell you to keep the user experience as consistent as possible across devices / platforms. Using the is attribute with this goal in mind has to add complexity in terms of markup, css, and API usage.

that is exactly why is="..." is superior to everything else. It works everywhere already, you can style it already, maybe the form won't be too fancy but its data will be submitted.

I think you misunderstood the benefits of the is="..." specially in terms of UX. Please read again this entire thread.

WebReflection commented Jul 3, 2018

Last thoughts:

The core reason we are using is is that we are dealing with user clients that may or may not support the custom element API. All UX / Experience designers will tell you to keep the user experience as consistent as possible across devices / platforms. Using the is attribute with this goal in mind has to add complexity in terms of markup, css, and API usage.

that is exactly why is="..." is superior to everything else. It works everywhere already, you can style it already, maybe the form won't be too fancy but its data will be submitted.

I think you misunderstood the benefits of the is="..." specially in terms of UX. Please read again this entire thread.

@oleersoy

This comment has been minimized.

Show comment
Hide comment
@oleersoy

oleersoy Jul 3, 2018

@oleersoy nobody gets hurt ever since the feature has been successfully polyfilled already for the last 4+ years. You are free to not use custom elements builtins, others are free to use those when needed.

When you create something more complex than what is needed that delivers and inferior and buggy ux experience with a paradigm that full of self contradiction there's a lot of pain that goes along with that.

Most people don't know that though. Tons of experienced developers used default exports. I suspect that we will have a 70% education rate about 10 years from now. Until then we will have produced billions of dollars worth of code and wasted billions of dollars of efforts to maintain and use something that could have been much simpler to start with had we gotten one simple concept right ...

oleersoy commented Jul 3, 2018

@oleersoy nobody gets hurt ever since the feature has been successfully polyfilled already for the last 4+ years. You are free to not use custom elements builtins, others are free to use those when needed.

When you create something more complex than what is needed that delivers and inferior and buggy ux experience with a paradigm that full of self contradiction there's a lot of pain that goes along with that.

Most people don't know that though. Tons of experienced developers used default exports. I suspect that we will have a 70% education rate about 10 years from now. Until then we will have produced billions of dollars worth of code and wasted billions of dollars of efforts to maintain and use something that could have been much simpler to start with had we gotten one simple concept right ...

@csuwildcat

This comment has been minimized.

Show comment
Hide comment
@csuwildcat

csuwildcat Jul 3, 2018

Ole, buddy, seriously:
giphy 1

csuwildcat commented Jul 3, 2018

Ole, buddy, seriously:
giphy 1

@oleersoy

This comment has been minimized.

Show comment
Hide comment
@oleersoy

oleersoy Jul 3, 2018

I'm starting my meditation session right now!!

oleersoy commented Jul 3, 2018

I'm starting my meditation session right now!!

@OvermindDL1

This comment has been minimized.

Show comment
Hide comment
@OvermindDL1

OvermindDL1 Jul 5, 2018

you are really free to not use is="..." in all cases where it's not mandatory (not LI, TD, SELECT, OPTION, etcetera)

Unless of course you want to use is for java-scriptless fallbacks (like elinks or other console browsers in my case) or for older screen-readers (like at work in my case).

OvermindDL1 commented Jul 5, 2018

you are really free to not use is="..." in all cases where it's not mandatory (not LI, TD, SELECT, OPTION, etcetera)

Unless of course you want to use is for java-scriptless fallbacks (like elinks or other console browsers in my case) or for older screen-readers (like at work in my case).

@hax

This comment has been minimized.

Show comment
Hide comment
@hax

hax Jul 6, 2018

Contributor

@oleersoy I totally agree what @WebReflection said. I only want to add comment about "default export".

Note, my comment is off-topic (we should discuss it in some repo under tc39 instead of w3c), but you alway use it as a evidence in the discussion, so I must point out that I totally disagree your criticism to default export in ES2015+.

  1. Default exports is nothing special, import x from ... is just a syntax sugar of import {default as x} from ..., so your judgment of it is harmful to refactoring can NOT be true. If IDE/tools did it wrong, they would also failed in the normal exports (at least in the cases of alias).

  2. IDE/tools can also do heuristic auto import for "default export"s by matching the name of default exports (normally classes and functions would have name) or the filenames (eg. parseFile would match path/to/parse-file.js if it has a default export).

  3. Yes someone may misuse default export to a huge object instead of multiple exports, just like misuse any language feature. What you really need is education, not ban the feature. And it could make tree shaking harder, but ideally a good enough tree shaking tool can also analysis it and know you only need one property on the export object, and eliminate the other part.

Summary: Default export may cause problems if your IDE/tools was not good enough and/or the education to your team members for this feature was not enough. Obviously, they are solvable. So as language spec perspective, nothing is wrong.

Contributor

hax commented Jul 6, 2018

@oleersoy I totally agree what @WebReflection said. I only want to add comment about "default export".

Note, my comment is off-topic (we should discuss it in some repo under tc39 instead of w3c), but you alway use it as a evidence in the discussion, so I must point out that I totally disagree your criticism to default export in ES2015+.

  1. Default exports is nothing special, import x from ... is just a syntax sugar of import {default as x} from ..., so your judgment of it is harmful to refactoring can NOT be true. If IDE/tools did it wrong, they would also failed in the normal exports (at least in the cases of alias).

  2. IDE/tools can also do heuristic auto import for "default export"s by matching the name of default exports (normally classes and functions would have name) or the filenames (eg. parseFile would match path/to/parse-file.js if it has a default export).

  3. Yes someone may misuse default export to a huge object instead of multiple exports, just like misuse any language feature. What you really need is education, not ban the feature. And it could make tree shaking harder, but ideally a good enough tree shaking tool can also analysis it and know you only need one property on the export object, and eliminate the other part.

Summary: Default export may cause problems if your IDE/tools was not good enough and/or the education to your team members for this feature was not enough. Obviously, they are solvable. So as language spec perspective, nothing is wrong.

@oleersoy

This comment has been minimized.

Show comment
Hide comment
@oleersoy

oleersoy Jul 6, 2018

Man I was meditating all peacefully --- Wooooza. OK - @hax suppose that the default export in ./exports.js which we currently refer to as doSomething in one import and doSmth in another as shown below..

// in file import1.js
import doSomething from "./exports.js"
// in file import2.js
import doSmth from "./exports.js"

Now the author of the export file decides to change the semantic meaning to something more specific like DoTheDew. How do the files importing the default export from ./exports.js get refactored with the new semantic meaning (DoTheDew.), and while attempting to answer that are you sure that you have considered all the permutations / contexts that doSomething and doSmth are used in?

Also since we could have used named exports to do the same job and it leads to more effective tree shaking, DX support, etc. did we do the people developing the tooling a favor by providing another option to do something that achieves the same result as named exports does, but is less effective at doing it?

Ultimately yes we may be able to develop tooling that deals with all the permutations of default exports, but this is a great deal of additional effort that would not be necessary if we just stick with named exports, which have additional benefits as it currently stands. There's no upside to default exports, just more cost, more uncertainty, and less standardization for developers. It introduces more complexity with zero upside. When you manufacture something you want to use as few parts as possible as it will simplify your design, have points of failure that are easier to detect, and will be easier to service. The same goes for software.

I'm repeating myself here (Sorry @csuwildcat - I tried), the parallel for that with the is attribute is that the is attribute implies two states for the client delivering whatever contains the is attribute. One where the is attribute falls back and another where the full capability is delivered. And the original assertion in the Google document I came across was that this was THE way to deliver progressive web apps, which is NUTS. Show me one app that can do this well (Stackblitz, Google Keep, ...) and is popular and I'll never comment again, and I know that just by me saying that there's 20 devs right now pulling 3 all nighters on this.

What you really need is education, not ban the feature.

You need a feature when there is no alternative. In this case there is and it's a lot better. And developers need a ton of education. If you want to be effective with Angular you need Typescript / Decorators, RxJS, CSS, Maybe Stylus, NodeJS, Git / Github, General Javascript, Travis, REST, GraphQL, NGRX/NGXS, ... I could go on and on. So if we can keep it simple and not add trivial non sense on top of the stack that's a good thing.

oleersoy commented Jul 6, 2018

Man I was meditating all peacefully --- Wooooza. OK - @hax suppose that the default export in ./exports.js which we currently refer to as doSomething in one import and doSmth in another as shown below..

// in file import1.js
import doSomething from "./exports.js"
// in file import2.js
import doSmth from "./exports.js"

Now the author of the export file decides to change the semantic meaning to something more specific like DoTheDew. How do the files importing the default export from ./exports.js get refactored with the new semantic meaning (DoTheDew.), and while attempting to answer that are you sure that you have considered all the permutations / contexts that doSomething and doSmth are used in?

Also since we could have used named exports to do the same job and it leads to more effective tree shaking, DX support, etc. did we do the people developing the tooling a favor by providing another option to do something that achieves the same result as named exports does, but is less effective at doing it?

Ultimately yes we may be able to develop tooling that deals with all the permutations of default exports, but this is a great deal of additional effort that would not be necessary if we just stick with named exports, which have additional benefits as it currently stands. There's no upside to default exports, just more cost, more uncertainty, and less standardization for developers. It introduces more complexity with zero upside. When you manufacture something you want to use as few parts as possible as it will simplify your design, have points of failure that are easier to detect, and will be easier to service. The same goes for software.

I'm repeating myself here (Sorry @csuwildcat - I tried), the parallel for that with the is attribute is that the is attribute implies two states for the client delivering whatever contains the is attribute. One where the is attribute falls back and another where the full capability is delivered. And the original assertion in the Google document I came across was that this was THE way to deliver progressive web apps, which is NUTS. Show me one app that can do this well (Stackblitz, Google Keep, ...) and is popular and I'll never comment again, and I know that just by me saying that there's 20 devs right now pulling 3 all nighters on this.

What you really need is education, not ban the feature.

You need a feature when there is no alternative. In this case there is and it's a lot better. And developers need a ton of education. If you want to be effective with Angular you need Typescript / Decorators, RxJS, CSS, Maybe Stylus, NodeJS, Git / Github, General Javascript, Travis, REST, GraphQL, NGRX/NGXS, ... I could go on and on. So if we can keep it simple and not add trivial non sense on top of the stack that's a good thing.

@hax

This comment has been minimized.

Show comment
Hide comment
@hax

hax Jul 6, 2018

Contributor

@oleersoy
Repeat again, default export is simply a "syntax sugar", so I don't think it will need "ton of education" in this simple case.

And default export definitely has the benefit -- follow the convention of node.js. Note, there is NO default export in the early ES6 draft, it is added because the node.js community gave the feedbacks. It may "zero upside" to you (and I admit I myself can live without it because I prefer AMD than CommonJS before ES module is standardized), but you can't deny node.js is a very important part of the js community so that default export have the benefit for the whole ecosystem.

Last, compare is attribute to default export is not suitable. Because is attribute is not a "syntax sugar". Without is, you have no way to utilize webcomponents on many builtin elements.

On the other side, if I need to ban a feature in a large engineer team, I would choose to ban the normal custom element, and only use is to extend. Because most average developers do not have enough knowledge and experiences on components design, which means they are very likely to produce a leaky abstraction, or do something wrong (for example, raise a11y issue). And is way is much safer for them. 😂

Contributor

hax commented Jul 6, 2018

@oleersoy
Repeat again, default export is simply a "syntax sugar", so I don't think it will need "ton of education" in this simple case.

And default export definitely has the benefit -- follow the convention of node.js. Note, there is NO default export in the early ES6 draft, it is added because the node.js community gave the feedbacks. It may "zero upside" to you (and I admit I myself can live without it because I prefer AMD than CommonJS before ES module is standardized), but you can't deny node.js is a very important part of the js community so that default export have the benefit for the whole ecosystem.

Last, compare is attribute to default export is not suitable. Because is attribute is not a "syntax sugar". Without is, you have no way to utilize webcomponents on many builtin elements.

On the other side, if I need to ban a feature in a large engineer team, I would choose to ban the normal custom element, and only use is to extend. Because most average developers do not have enough knowledge and experiences on components design, which means they are very likely to produce a leaky abstraction, or do something wrong (for example, raise a11y issue). And is way is much safer for them. 😂

@oleersoy

This comment has been minimized.

Show comment
Hide comment
@oleersoy

oleersoy Jul 6, 2018

Repeat again, default export is simply a "syntax sugar", so I don't think it will need "ton of education" in this simple case.

Not true. It allows us to redefine randomly what a module exports when we import it. That then can lead to multiple collisions when attempting to refactor in order to adopt a new naming convention.

  1. Something as simple a going from camel case to underscores becomes impossible.
  2. The risk of refactoring naming collisions goes up drastically because we now have random names all over the place.
  3. Tree shaking a massive object that a junior developer created requires either new tooling (Which takes a while to fix - Check rollup or parcel issue log) or we just don't do it.

We could easily avoid all these and the repercussions by using named exports.

oleersoy commented Jul 6, 2018

Repeat again, default export is simply a "syntax sugar", so I don't think it will need "ton of education" in this simple case.

Not true. It allows us to redefine randomly what a module exports when we import it. That then can lead to multiple collisions when attempting to refactor in order to adopt a new naming convention.

  1. Something as simple a going from camel case to underscores becomes impossible.
  2. The risk of refactoring naming collisions goes up drastically because we now have random names all over the place.
  3. Tree shaking a massive object that a junior developer created requires either new tooling (Which takes a while to fix - Check rollup or parcel issue log) or we just don't do it.

We could easily avoid all these and the repercussions by using named exports.

@oleersoy

This comment has been minimized.

Show comment
Hide comment
@oleersoy

oleersoy Jul 6, 2018

On the other side, if I need to ban a feature in a large engineer team, I would choose to ban the normal custom element, and only use is to extend. Because most average developers do not have enough knowledge and experiences on components design, which means they are very likely to produce a leaky abstraction, or do something wrong (for example, raise a11y issue). And is way is much safer for them.

And what are you doing in all the cases where the is attribute is not applicable because the element can't be downgraded to something, which is essentially 99.9999999% of all developer cases that custom elements were developed for to start with?

oleersoy commented Jul 6, 2018

On the other side, if I need to ban a feature in a large engineer team, I would choose to ban the normal custom element, and only use is to extend. Because most average developers do not have enough knowledge and experiences on components design, which means they are very likely to produce a leaky abstraction, or do something wrong (for example, raise a11y issue). And is way is much safer for them.

And what are you doing in all the cases where the is attribute is not applicable because the element can't be downgraded to something, which is essentially 99.9999999% of all developer cases that custom elements were developed for to start with?

@FND

This comment has been minimized.

Show comment
Hide comment
@FND

FND Jul 7, 2018

At the risk of prolonging this ... interaction, you might appreciate @adactio's Just what is it that you want to do? (and corresponding video).

FND commented Jul 7, 2018

At the risk of prolonging this ... interaction, you might appreciate @adactio's Just what is it that you want to do? (and corresponding video).

@oleersoy

This comment has been minimized.

Show comment
Hide comment
@oleersoy

oleersoy commented Jul 23, 2018

Some additional material related to developer education:

https://hackernoon.com/how-it-feels-to-learn-javascript-in-2016-d3a717dd577f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment