Preferences

So, the article doesn't discuss this, but there's actually a really good reason to make up and use custom elements: the browser can hydrate their dynamic behaviour automatically. For example, suppose you have:

    <div class=expander>
      <button aria-expanded=false>Expand</button>
      <!-- Some other stuff here -->
    </div>
And you have some JS that handles the expander's behaviour:

    for (const expander of document.querySelectorAll('.expander')) {
      const btn = expander.querySelector('button');

      btn.addEventListener('click', () => {
        btn.ariaExpanded = 'true';
      });
    }
This will work fine for `.expander` divs that are already in the page when the event handler is set up. But suppose you dynamically load new expander divs, what then? Your event handler is not going to retroactively set up their click listeners too.

Custom elements solve exactly this problem. You can now do:

    <expander-elem>
      <button aria-expanded=false>Expand</button>
      <!-- Some other stuff here -->
    </expander-elem>
And then set up the listener:

    customElements.define('expander-elem', class extends HTMLElement {
      connectedCallback() {
        const btn = this.querySelector('button');

        btn.addEventListener('click', () => {
          btn.ariaExpanded = 'true';
        });
      }
    });
And the browser will ensure that it always sets up the listeners for all of the expanders, no matter whether they are loaded on the page initially or dynamically injected later. Without this you would have had to jump through a bunch of hoops to ensure it. This solves the problem elegantly.

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.

I blogged about this long time ago: https://dev.to/dannyengelman/web-component-developers-do-not...

Well, that's why I include JS files at the bottom of my HTML body, to make sure to avoid exactly this problem: https://github.com/yawaramin/dream-html-ui/blob/92f2dfc51b75...
One drawback.

► execute <script> at bottom of file

► execute <script defer>

Both do the same; they execute script after DOM was parsed. When your JS creates GUI you now have to battle FOUCs.

► "import" loads your script async

so it _could_ load before _all_ DOM has parsed... but 9999 out of 10000 scenarios it won't

That's why my JS doesn't create the GUI, it just attaches event handlers to the GUI rendered on the server. Check the link I posted above...
Beware that connectedCallback runs _every time_ a custom element is added to the dom. So you should make sure to only add event listeners once by tracking internally if the element was already initialized.
Thanks, that's good to know. In some contexts I don't think a custom element is ever added to the DOM more than once, eg in htmx I am responding with HTML markup which is then injected into the page and then just kept there, possibly 'forever' or at least until it is replaced by some new markup from a new response.
Yes, the hydration behavior of custom elements is nice. You don’t even need to do anything special with JS bundle loading.

Simply render your <element> (server-side is fine) and whenever the JavaScript downloads and executes your custom elements will mount and do their thing.

That's good to know, and I even did say that custom UX widgets are a case where custom tag names makes more sense conceptually.

I'm really just pushing back on the idea of using them for CSS formatting purposes of general text and layout instead of classes.

This item has no comments currently.

Keyboard Shortcuts

Story Lists

j
Next story
k
Previous story
Shift+j
Last story
Shift+k
First story
o Enter
Go to story URL
c
Go to comments
u
Go to author

Navigation

Shift+t
Go to top stories
Shift+n
Go to new stories
Shift+b
Go to best stories
Shift+a
Go to Ask HN
Shift+s
Go to Show HN

Miscellaneous

?
Show this modal