Browsers create ANY tag with at least one dash as a *Custom Element*
They come in TWO flavours, and since they ARE HTMLElement, can be used for layout AND styling with CSS.
The official names are:
► UNdefined Custom Elements (the article calls these "CSS Web Components")
- shadowDOM optional with Declarative ShadowDOM
► Defined Custom Elements
- Defined with the JavaScript Custom Elements API
- shadowDOM optional
- A new Element, or an UPGRADED existing UNdefined Custom Element
---
### Good to know about UNDEFINED Custom Elements:
* Absolutely NO JavaScript required, it is only HTML and CSS
* This is STANDARD behaviour in all browsers for nearly a DECADE now:
Chrome (2016) Safari (2017) FireFox (2018)
* The W3C HTML Validator accepts ALL <tag-name> Custom Elements with a dash as HTMLElement.
It does not accept <tagname> (no dash), those are HTMLUnknownElement
* Custom Elements do not inherit the standard [hidden] behaviour;
so you have to add that behaviour yourself in your stylesheet.
* Same for DIVs display:block. You have to set the display property on these Custom Elements yourself.
(You will forget this 20 times, then you never make the mistake again)
* The :defined pseudo selector targets standard HTML tags and JavaScript defined Custom Elements
* Thus :not(:defined) targets the UNdefined Custom Elements; again... they are still valid HTMLElement so CSS applies like any element
* <you-are-not-bound-to-one-dash>
* Declarative ShadowDOM <template shadowrootmode="open"> creates the same UNdefined Custom Elements WITH a shadowDOM
* The Custom Elements JavaScript API upgrades UNdefined Custom Elements TO defined Custom Elements.
* You can't UNdefine defined Custom Elements
* You can't REMOVE a set shadowRoot
* for now, only Safari supports multiple custom element registries (duplicating Custom Element names)
----
Why?
► Try to find that closing </div> in a long HTML page. </tag-name> is always just there.
► Built a UI that doesn't FOUC, and UPGRADE it lazy-loaded with more logic and interactivity... you can not do this with technologies that CREATE HTML AFTER DOM was parsed.
Custom Elements/Web Components ARE HTML; Frameworks CREATE HTML
We will forever call Custom Elements: Web Components, and vice versa...
◄ <tagname> = always an HTMLUnknownElement until the WHATWG adds it as new Element.
◄ <tag-name> = (No JS!) UNDEFINED Custom Element, valid HTMLElement, great for layout and styling
◄ Upgraded with the JavaScript Custom Elements API it becomes a DEFINED Custom Element
---
► This is standard behaviour in all browsers. Chrome (2016) Safari (2017) FireFox (2018) Edge (2020)
► The W3C HTML Validator accepts all <tag-name> Custom Elements with a dash as HTMLElement.
It does not accept <tagname> (no dash), those are HTMLUnknownElement
► The UA - UserAgent StyleSheet (Browsers default stylesheet)
defines CSS [hidden] { display:none }.
But Custom Elements do not inherit the default stylesheet;
so you have to add that behaviour yourself in your stylesheet.
► <DIV> is display:block only in the UA StyleSheet
You have to set the display property on these Custom Elements yourself
(You will forget this 20 times, then you never make the mistake again)
► The CSS :defined pseudo selector targets standard HTML tags and JavaScript defined Custom Elements
► Thus the CSS :not(:defined) pseudo selector targets the UNDEFINED Custom Elements; they are still valid HTMLElement, CSS applies like any element
► DSD - Declarative ShadowDOM:
<template shadowrootmode="open"> creates the same undefined Custom Elements with a shadowDOM
> The UA - UserAgent StyleSheet (Browsers default stylesheet) defines CSS [hidden] { display:none }
I can only speak for Chromium, but this isn't about the UA stylesheet; everything in the UA stylesheet applies to custom elements just the same as the more standard HTML elements (e.g. the rule for [popover] will apply just fine to custom elements AFAIK), and there is no [hidden] rule in the UA stylesheet. You're probably mixing it up with the fact that hidden is a HTML presentation attribute, similar to how you can write <div align="right"> and it will become its own little property value set that gets applied to the element. That is indeed only the case for HTMLElements. (The difference matters for priority in the cascade; in particular, presentation attribute style has zero specificity.)
I'm a bit miffed about the dash. I wish it was a colon. Then well established XML could be simply name-spaced in, and then either styled with css and enhanced with JS. I suspect it wouldn't be that difficult to write something for nginx or apahce that simply converted the colon to a hyphen. Oh well, it cannot be 1999 forever.
Mainly because it isn't semantic and breaks accessibility features. If you find yourself writing layouts like this you're probably ignoring a bunch of useful stuff like <aside> <article> <menu> etc. Unless you manually configure it yourself, screen readers won't know what's important to read, tabindex won't know where to jump around, and form fields won't know what values to offer.
It's certainly better than calling everything a div.
> breaks accessibility features
I don't know if I'd call it breakage to just... not use them where they should be used. Of course if a real tag exists that adequately matches the author's intent, that should be preferred over a made-up one.
> It's certainly better than calling everything a div.
It's not. For semantic purposes <my-element> is the same as <div class=my-element>. So on the surface they are equivalent.
But if you are in the habit of using custom elements then you will likely continue to use them even when a more useful element is available so <my-aside> rather than <aside class=my-aside> so in practice it is probably worse even if theoretically identical.
Basically divs with classes provide no semantic information but create a good pattern for using semantic elements when they fit. Using custom elements provides no semantic information and makes using semantic elements look different and unusual.
> But if you are in the habit of using custom elements then you will likely continue to use them even when a more useful element is available
This article is written for web developers. I’m not sure who you think you are addressing with this comment.
In any case - the argument is a weak one. To the extent people make the mistake you allege they can make it with classed div and span tags as well and I’ve seen this in practice.
That is a strawman. I never said everyone who uses classes perfectly uses semantic elements.
My point is that if you are using <div class=my-element> you don't have to change your .my-element CSS selector or JS selection code to improve your code to <p class=my-element>. If you are using <my-element> it is much more work to change your selectors and now you have two ways of doing things depending on if you are using a native semantic element or a div (either a tag selector or class selector). You have made your styling code depend on your element choice which makes it harder to change.
this.querySelector will return nothing when you define this Web Component before (light)DOM is parsed, because the connectedCallback fires on the opening tag.
Above code will only work when the Web Component is defined after DOM has parsed; using "defer" or "import" makes your JS file execute after DOM is parsed, you "fixed" the problem without understanding what happened.
The article isn't complete/correct.
Something did change with HTML.
Since 2018 every browser interprets ANY <tag-name> with a dash as a valid HTMLElement, not HTMLUnknownElement.
Absolutly NO JavaScript required to turn the DIV-soup into
<semantic-html> and CSS
> there are no weird rendering glitches or timing issues or weird gotchas that you have to dig into to.
Ehm... define the Web Component render blocking in the head, because you want to prevent FOUCs.
Then try to access the .innerHTML of your Web Component in the connectedCallback
Interesting but I wouldn't call that a gotcha. Trying to read the DOM outside of the component is kind of an anti-pattern and I think this kind of behavior should be expected. You'd generally want the web component to be a black box and it works pretty well once you understand the rendering order and focus only on internal or slotted elements. I think the only real 'gotcha' I came across was the order of when the attribute changed callback gets called versus the connect callback but it's easy to adjust the logic internally to take this into account. I can make do with a custom render() method and sometimes additional methods to perform specific granular updates to the component's own DOM...
With React, the component either gets rendered or it doesn't get rendered; all or nothing and React fully controls this. With web components you can also just nit-pick a single element within the component's DOM and only update that one. It gives you more opportunities in terms of performance optimization and how you split up your components... But you can also achieve a similar result as React by just re-rendering the whole component if necessary.
The one difficulty which is actually an advantage IMO is that you cannot pass JavaScript object or function references to Web Components via HTML attributes; you have to pass primitive values... But IMO this a BENEFIT because it forces separation of concerns and it makes the DOM way easier to debug in the dev console. For advanced use cases you can still pass JS refs to a child component by calling methods on it but I try to avoid this because it makes it harder to understand the state of a component.
I like it when I can understand the state of a component just by looking at its attributes and I like those values to be as simple and human-readable as possible. I love opening up my dev console and just tweaking the HTML attributes directly and see the change happening. Makes debugging a breeze. Kind of tricky to do with React and you get into all sorts of weird situations where a render may not trigger when you expect when you change properties of an object without changing the reference itself. React adds a lot of unnecessary complexity there.
I would go all-in.
Web Components is not (just) about technology, but about 4 companies working together on setting a standard.
Companies that used to fight in the Browser Wars now work together on the standard.
They are the WHATWG that took the role of the W3C HTML/Web workinggroup.
4 companies ... Apple, Google, Mozilla and Microsoft
And the WHATWG (for now) is "by invitation only"... note the big name missing.
Browsers create ANY tag with at least one dash as a *Custom Element*
They come in TWO flavours, and since they ARE HTMLElement, can be used for layout AND styling with CSS.
The official names are:
► UNdefined Custom Elements (the article calls these "CSS Web Components")
► Defined Custom Elements - Defined with the JavaScript Custom Elements API ---### Good to know about UNDEFINED Custom Elements:
* Absolutely NO JavaScript required, it is only HTML and CSS
* This is STANDARD behaviour in all browsers for nearly a DECADE now: Chrome (2016) Safari (2017) FireFox (2018)
* The W3C HTML Validator accepts ALL <tag-name> Custom Elements with a dash as HTMLElement. It does not accept <tagname> (no dash), those are HTMLUnknownElement
* Custom Elements do not inherit the standard [hidden] behaviour; so you have to add that behaviour yourself in your stylesheet.
* Same for DIVs display:block. You have to set the display property on these Custom Elements yourself. (You will forget this 20 times, then you never make the mistake again)
* The :defined pseudo selector targets standard HTML tags and JavaScript defined Custom Elements
* Thus :not(:defined) targets the UNdefined Custom Elements; again... they are still valid HTMLElement so CSS applies like any element
* <you-are-not-bound-to-one-dash>
* Declarative ShadowDOM <template shadowrootmode="open"> creates the same UNdefined Custom Elements WITH a shadowDOM
* The Custom Elements JavaScript API upgrades UNdefined Custom Elements TO defined Custom Elements.
* You can't UNdefine defined Custom Elements
* You can't REMOVE a set shadowRoot
* for now, only Safari supports multiple custom element registries (duplicating Custom Element names)
----
Why?
► Try to find that closing </div> in a long HTML page. </tag-name> is always just there.
► Built a UI that doesn't FOUC, and UPGRADE it lazy-loaded with more logic and interactivity... you can not do this with technologies that CREATE HTML AFTER DOM was parsed.
Custom Elements/Web Components ARE HTML; Frameworks CREATE HTML
We will forever call Custom Elements: Web Components, and vice versa...