WICG / webcomponents

Web Components specifications
Other
4.39k stars 375 forks source link

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

Closed trusktr closed 8 years ago

trusktr commented 8 years ago

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).

WebReflection commented 7 years ago

WebGL fragment and vertex examples:

trusted sources https://www.html5rocks.com/en/tutorials/webgl/webgl_fundamentals/ https://www.khronos.org/webgl/wiki/Tutorial https://msdn.microsoft.com/en-us/library/dn385809(v=vs.85).aspx

how users do that anyway https://github.com/mrdoob/three.js/issues/283 http://stackoverflow.com/questions/4878145/javascript-and-webgl-external-scripts https://blog.mayflower.de/4584-Playing-around-with-pixel-shaders-in-WebGL.html

Can they do it differently? Yes. Will they? Probably no, they don't care, script works already like a charm.

What would is solve for them? It'd be a better way to bootstrap shaders without needing to pollute the global context with unique IDs all over.

Does it mean we probably just need a native mechanism to bootstrap once some native element on the DOM? Most likely for this specific case, and coincidentally, is does that too.

Best Regards

oleersoy commented 7 years ago

First of all let me say that these examples and articles are very cool. I'm going to have to do a deep dive on these!

What would is solve for them? It'd be a better way to bootstrap shaders without needing to pollute the global context with unique IDs all over.

This is a good point. Definitely seems like we can do better here. As you have probably noticed, I lean more to the dedicated custom element side, along with schema relaxations on the HTML spec so that we can place elements where we need to place elements, but this is the best example I have seen so far to convince me that is might actually be valuable. Thanks.

prushforth commented 7 years ago

I am studying Custom Elements v1 right now, and I have to say I think that the is="" syntax is quite natural. I don't think HTML authors will be confused by this, as it is not actually JavaScript at that point and so the class inheritance business is not really in the mind of the HTML author. However, inheriting the API of the element which you're extending could be a great benefit, if it can be done technically. Even if it can't, executing the behaviour of the element in the case JS didn't execute properly would still make it worthwhile, because that is a "fallback"; I don't see how that could be done without using the original element. That is all. Thanks for Custom Elements, I think they are great. Keep up the good work!

trusktr commented 7 years ago

If we keep it on merit of understandable syntax, then why not keep it in the same direction as JavaScript?

<an-element extends="other-element"></an-element>

The following would be easier to understand:

class Foo extends Bar { ... }
customElements.define('foo-element', Foo)

function New(base) {
  return class New extends base { ... }
}
customElements.define('new-element', New)

could be the implementation of what happens when like

<new-element extends="foo-element" ...>
  ...
</new-element>

is written, where foo-element's constructor will be passed into new-element's mixin. Maybe learning classical programming in HTML would be easier this way (keeping it all in the same direction as JavaScript).

trusktr commented 7 years ago

I forgot to mention that the following should be possible too:

<some-element extends="video" ...>
  ...
<some-element>

with emphasis on the fact that the built-in element goes inside the extends property.

I argue the same if we keep is="":

<new-element is="foo-element" ...>
  ...
</new-element>
<some-element is="video" ...>
  ...
<some-element>
trusktr commented 7 years ago

Sorry, one more thing: For people who like fallbacks, in

<some-element is="video" ...>
  ...
<some-element>

, <some-element> can behave the same as a <video> element when there is not a definition for some-element.

ghost commented 7 years ago

@trusktr

Well, that could work, but that would probably be a lot harder to parse — and browser vendors have already shown their disinterest in changing the parser —, it wouldn’t be as convenient, it would be harder (even if slightly) to understand, it wouldn’t be immediately clear by the looking at the tag name that <my-foo extends="table"> would act as a table, and your fallback would not work for old user agents that don’t support custom elements.

oleersoy commented 7 years ago

The thing that I think is really strange about this still is that we now have to explain the progressive enhancement concept to new developers who may have just learned html and have put in charge of assembling both custom elements and current html spec elements into a page.

The first thing that struck me is that it sounds like we are now OK with FOUC. Before it was something that we would hide until all resources are loaded and everything works and now it almost seems like FOUC is fine. The second thing is that we now have to explain that if you have <my-foo> and <your-foo> and they are supposed to work together then just forget the whole thing. So now the explanation is already starting to sound like a CrazyHack

CSS and html can both be pretty daunting when first starting out. And now we are adding a fairly massive switch to that, and it can lead to a lot of confusion.

If you have a penny and flip it you have 2 different results. Pretty easy to understand. If you have two you can have 4. If we have 3 then we can have 8, and so on. For each penny we add there's lot more combinations grapple with.

We could say that we are only going to use is in 1 penny type situations, but if that's the case can't we come up with simple? What about just having <my-foo> and it works because we have precached the resources that make it work. End of story. We took one penny out of the learning curve for all future developers.

tomalec commented 7 years ago

Regarding:

it wouldn’t be immediately clear by the looking at the tag name that <my-foo extends="table"> would act as a table

and the problems with handling the attribute (is, or extends) in some special way, with CE reactions in place

Maybe we could think of just another syntax, like <table+my-foo>...</table>, then

element.attributes // []
element.localName // my-foo
element.tagName // table

Pros:

Cons:

EduardoRFS commented 7 years ago

To me this issue is not more a issue, ìs= is not necessary polymer 2 has proved. Using a simple wrapper, we have access to all benefits of "extending", support in webkit, semantic markup and not redundant code.

ghost commented 7 years ago

@EduardoRFS

Well, ou can’t “extend” table‐related elements with wrappers.

prushforth commented 7 years ago

@trusktr My point is that HTML and JS are not the same language, so HTML doesn't owe any conceptual tip of the hat to JS. In that light, <button is="my-button"> is quite natural for HTML authors.

Regarding 'Easier to understand', it is perhaps true (of your example) for JS programmers, but when you start writing for the Web you don't actually start with JS, you start with HTML, so 'easier to understand' probably matters more for the true novice more than someone who has progressed to object-oriented JS, and should therefore emphasize HTML syntax.

If the JS fails to execute in the <button is="my-button"> example, for whatever reason, you still get a button, not to mention the accessibility considerations others have raised and can speak to better than I can.

I wrote a post about this, with a working example, using the <map is="web-map"> custom element.

OvermindDL1 commented 7 years ago

@EduardoRFS

To me this issue is not more a issue, ìs= is not necessary polymer 2 has proved. Using a simple wrapper, we have access to all benefits of "extending", support in webkit, semantic markup and not redundant code.

Except it is both longer to write a wrapper rather than an is=, and it still does not work on, for example, table elements (a very common reason to have a repeater for example), or templates.

oleersoy commented 7 years ago

My point is that HTML and JS are not the same language, so HTML doesn't owe any conceptual tip of the hat to JS. In that light,

Maybe but it brings up a question. Why not just do <my-button>? It's shorter. It's more DRY. It's an element. HTML developers know what elements are. We don't have to explain what the is is for and it offers a cleaner and simpler development path than the indirection brought up by is.

The other question becomes when do we do <my-button> and when do we do <button is="my-button">?

I get that with is we still get a button, but that button has to do something. With is does something it interacts with other things. If we are using is we are introducing a fork in the development approach. If the API that comes with <my-button> has not loaded then the development approach needed for a satisfying customer experience is completely different. Now we need two people to screw in the lightbulb. We have to write more design documentation. We have to write more tests. Etc. Etc. Etc.

tomalec commented 7 years ago

The other question becomes when do we do <my-button> and when do we do <button is="my-button">?

(mostly as already mentioned above, like in Eric's post) As the author of my-button you prepare it to be

I probably missed few other reasons, bu I think it's quite enough to express value of is.

oleersoy commented 7 years ago

@tomalec these might be good reasons. Lets do them one by one.

you need element's specific parsing context (template, table, script, meta,... dozens of others) ,

Can you give me a simple real world example of when you need one of these and point out how it is better than using the corresponding custom element by itself? BTW - I do understand that if you place a custom element within a table the browser will boot it. But is that the only reason?

@WebReflection also gave a really good example for shaders. And I agree with him that in the current state of things is is useful in the context he gave. But I still think we can do even better. For example if we get a schema relaxtion switch so that we can place elements in table container then we can produce shaders in custom elements that are developer friendly and minimize project delivery time, browser lines of code, etc.

OvermindDL1 commented 7 years ago

@oleersoy

But is that the only reason?

Progressive fallback. <my-decorated-button> will not work when it is an old browser where

oleersoy commented 7 years ago

@OvermindDL1 is that not what Polyfills are for?

OvermindDL1 commented 7 years ago

@OvermindDL1 is that not what Polyfills are for?

You seem to be assuming that javascript is available. I often have to use browsers like elinks due to lack of a graphical interface. There is no javascript there, just pure html, the site should at least remain 'functional' if ugly.

oleersoy commented 7 years ago

You seem to be assuming that javascript is available.

If you are designing using custom elements for a wide user base, then yes.

oleersoy commented 7 years ago

There is no javascript there, just pure html, the site should at least remain 'functional' if ugly.

What does functional mean? In other words it allows you to do what?

WebReflection commented 7 years ago

You can create a <table is="sort-table"> with <tr is="draggable-row"> inside and printers, as well as older browsers, will be able to see data regardless.

This is so simple as concept, and yet I have the feeling that by the time you'll get it it's 2020 and we won't need HTML at all ... is anything happening about this part of the specification?

Chromium seems close but I don't see progresses, the only paying attention to this thread are those not involved in the standardization process.

oleersoy commented 7 years ago

You can create a <table is="sort-table"> with <tr is="draggable-row"> inside and printers, as well as older browsers, will be able to see data regardless.

I understand this argument, but the thing I don't understand is why you feel that it is so important? You walk into a Walmart and high definition 4K displays are practically being thrown at us. Suppose someone has a really old phone and for some reason the display of the table is not coming up. They can just tap the person next to them and borrow her Google Pixel ....

This is so simple as concept, and yet I have the feeling that by the time you'll get it it's 2020 and we won't need HTML at all ... is anything happening about this part of the specification?

That's exactly the point. By the time this is implemented natively by Apple and Google, if ever, no one is going to need this.

And by need I mean 'need' as in Oxygen. Most of us have a requirement for that. I'm not trying to be be 'smart', I'm just pointing out that there are things that are hard requirement, and then there are things that are nice to have, like a Ferrari, but most of us are pretty happy without it still, although I could definitely use a Ferrari.

prushforth commented 7 years ago

My point is that HTML and JS are not the same language, so HTML doesn't owe any conceptual tip of the hat to JS. In that light, is quite natural for HTML authors.

Maybe but it brings up a question. Why not just do ? It's shorter. It's more DRY.

How is it more DRY? And are you arguing that character count matters here?

It's an element. HTML developers know what elements are. We don't have to explain what the is is for and it offers a cleaner and simpler development path than the indirection brought up by is.

Yes it’s an element, in a namespace. It will stay in that namespace forever. That’s a choice you make when you begin development: is this meant as an iteration of the platform, or does it belong to my organization?

oleersoy commented 7 years ago

How is it more DRY? And are you arguing that character count matters here?

Personally I think it's a bit silly and I have had debates about it with people in the past. But a lot of devs do feel strongly that it matters. Just look at the debates on Javascript module import Syntax. We have the fat arrow because it cuts down on character count, etc.

Specifically: customElements.define('fancy-button', FancyButton);

Is more DRY than:

customElements.define('fancy-button', FancyButton, {extends: 'button'});

prushforth commented 7 years ago

How is it more DRY? And are you arguing that character count matters here?

Personally I think it's a bit silly and I have had debates about it with people in the past. But a lot of devs do feel strongly that it matters. Just look at the debates on Javascript module import Syntax. We have the fat arrow because it cuts down on character count, etc.

Specifically: customElements.define('fancy-button', FancyButton);

Is more DRY than:

customElements.define('fancy-button', FancyButton, {extends: 'button'});

The first example doesn’t extend anything. If the extends option actually causes the inheritance of some characteristics, the amount of code that would have to be written and transmitted to make up for the lack of inheritance more than accounts for the few extra bytes, I think.

oleersoy commented 7 years ago

The first example doesn’t extend anything.

It does (From the V1 spec documentation):

// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
// for the list of other DOM interfaces.
class FancyButton extends HTMLButtonElement {
  constructor() {
    super(); // always call super() first in the ctor.
    this.addEventListener('click', e => this.drawRipple(e.offsetX, e.offsetY));
  }

  // Material design ripple animation.
  drawRipple(x, y) {
    let div = document.createElement('div');
    div.classList.add('ripple');
    this.appendChild(div);
    div.style.top = `${y - div.clientHeight/2}px`;
    div.style.left = `${x - div.clientWidth/2}px`;
    div.style.backgroundColor = 'currentColor';
    div.classList.add('run');
    div.addEventListener('transitionend', e => div.remove());
  }
}
WebReflection commented 7 years ago

@oleersoy maybe you haven't realized that this basic case breaks already:

class MyButton extends HTMLButtonElement {}
new MyButton();

When we say native extends matters we mean also the basic ability to extend native functionality inheriting from them.

At this stage only HTMLElement is subclassable, everything else is plain broken.

It's like being able to extend Object but not Array ... if you are happy with that, I am happy for you ... but logically speaking, nobody should be happy about such weirdo-breaking situation.

prushforth commented 7 years ago

@oleersoy @WebReflection The email reply system is broken but essentially this was my comment. You can only subclass HTMLElement right now. Furthermore, the reason one needs to identify which element you intend to extend is because elements can share script interfaces:

In general, the name of the element being extended cannot be determined simply by looking at what element interface it extends, as many elements share the same interface (such as q and blockquote both sharing HTMLQuoteElement).

So I guess the JavaScript engine has to know which element to create (?) in the DOM given it has multiple potential elements to pick from. Given all that I grant you it is something for programmers to learn/understand.

Apologies all of the formatting is lost when you reply by email - won't do that again :-)

oleersoy commented 7 years ago

@oleersoy maybe you haven't realized that this basic case breaks already: At this stage only HTMLElement is subclassable, everything else is plain broken.

How about we fix that then? It's a spec and we are in new territory either way. It's like putting an addition on the house. We can change the spec. Maybe tear out the kitchen. Etc.

OvermindDL1 commented 7 years ago

How about we fix that then? It's a spec and we are in new territory either way. It's like putting an addition on the house. We can change the spec. Maybe tear out the kitchen. Etc.

As long as it is backwards compatible enough to allow progressive enhancement enough that old non-js browsers can still be supported without needing to write duplicate custom sites for them. I.E., a

oleersoy commented 7 years ago

browsers can still be supported without needing to write duplicate custom sites for them

That's the key. And that's why I keep asking for simple examples of web content that uses custom elements and the is attribute, but will still provides a decent user experience without javascript. If that's possible then there is a point here, so the more user stories / cases we collect the better the argument gets.

I think the reason browser vendors are expressing a distaste for the is attribute is because it sounds nice in theory, but will actually never be used by 99.99% of developers because the will just be using things like <paper-button> instead. It's well document, provides rich functionality, and it's an element that's easy to understand and use.

prushforth commented 7 years ago

Maybe you missed my example of a type extension of the <map is="web-map"> element, which I posted above, but here it is again, just in case.

http://maps4html.github.io/Web-Map-Custom-Element/blog/progressive-web-maps.html

Here is another demo page

http://maps4html.github.io/Web-Map-Custom-Element/

oleersoy commented 7 years ago

Nice article but it does not exactly scream simple to me. Can I just put <map is="web-map"> and get a working map if javascript is turned off?

prushforth commented 7 years ago

Try it! (On the blog post at least, I can't remember if I put the image in the demo page) you should get the essential user experience of a map, minus zooming and panning. There's probably more I could/should do.

oleersoy commented 7 years ago

I tried it, I like it, and I think it's very cool! But when do we use it and how? I don't think this is going to pass @OvermindDL1 criteria, because when javascript is turned off how do you get the map tiles?

prushforth commented 7 years ago

You get the image map, which is acceptable but not ideal.

oleersoy commented 7 years ago

OK I think it's cool and a great experiment, but if I tried to push it on my daughter she'd be like "Dad - I'm going with Waze". It's cool - but still!!

prushforth commented 7 years ago

LOL yes I get that. Hopefully the Waze guys will use it someday. I mean the comment system here is using HTML but you wouldn't know it mostly.

oleersoy commented 7 years ago

Kids these days don't appreciate good engineering! :)

WebReflection commented 7 years ago

We can change the spec. Maybe tear out the kitchen. Etc.

wait ... you quoted the current specs and this entire thread is about not changing them because already OK but some vendor author's manager doesn't get it.

There is nothing to change in specs because as these are, ... drum rolls ..., the bloody is attribute should be already there and widely deployed!

it's in the specs already

oleersoy commented 7 years ago

OK but some vendor author's manager doesn't get it

You mean Apple?

you quoted the current specs and this entire thread is about not changing them

OK so if I want to join the party of coolaid drinkers then it's OK to comment, but otherwise be quiet?

should be already there and widely deployed!

So are AngularJS 1.0 Apps. These things change very fast.

chaals commented 7 years ago

People, be nice please.

oleersoy commented 7 years ago

@WebReflection started it! :)

chaals commented 7 years ago

@trusktr wrote

If we keep it on merit of understandable syntax, then why not keep it in the same direction as JavaScript?

That's not the primary reason for it. The primary reason is the HTML parser - which gives special magic to elements having their native HTML name.

I agree with others that the syntax is kind of ugly, but it solves a specific concrete problem. The extends stuff would be nice - not least because you could make it do mix-in things to extend both tr and input type="date" in a single custom element. But that seems more like a nice new thing than solving an existing problem.

WebReflection commented 7 years ago

OK so if I want to join the party of coolaid drinkers then it's OK to comment, but otherwise be quiet?

no, you made your single use case "CE are good enough" point, and never been able to answer all other problems already solved, and please let me underline already thanks to polyfills, by builtins extends.

You are in the same list of browser vendors that complain about is="" attribute and never provided an alternative.

We all got your point, which is restated over and over, and it keeps ignoring all counter-points that invalidate the "CE are good enough" argument.

Do you have a proposal to fix and cover what is="" attribute does already?

Yes? Please share!

No? Please stop making noise in this thread.

Thank you! (and I'll stop making noise myself too now, enough is enough)

oleersoy commented 7 years ago

Do you have a proposal to fix and cover what is="" attribute does already?

Yes. Do exactly what you are doing now.

No? Please stop making noise in this thread.

Right - my comments are noise and your comments are pure genius.

oleersoy commented 7 years ago

Thank you! (and I'll stop making noise myself too now, enough is enough)

No need to thank me. You are in full control of your own comments. If you think insulting me and chalking up my comments to "Noise" makes me go away, you are going to be in for rude awakening. Personally I'm just getting warmed up.

OvermindDL1 commented 7 years ago

Yes. Do exactly what you are doing now.

Well currently I am using is=, so keep using it then? I do need my management site to be accessible from non-javascript browsers after all and using pure custom elements would break that.

oleersoy commented 7 years ago

Well currently I am using is=, so keep using it then?

Yes absolutely. It's currently being polyfilled in almost all the browsers that load your pages, so just use it. We change the spec so that it's DRY and maintain polyfill for all the cases that you need is for.