Shadow DOM "the right way" in 2024 - Isolation, not complication 🤷
Tags: webdev, webcomponents
Published: 2024-01-16 07:18:51

For many years, I've read about **Shadow DOM** in Web Component from many places then just wonder why the implementation is so complicated.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4uiup17svyqcwqz1d2jj.png)

While I'm working on Adaptive Style Component Framework: [Adapter](https://keenlycode.github.io/adapter/). I found many complicated things when I tried to implement style with Shadow DOM encapsulation. That make me rethink again, is there any easier way to use Shadom DOM ?

---

# To the solution

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r85x2pr9yf9jjs059gl3.png)

If you want to get into the solution right away, this is the idea codes (Not the ideal one but you can get the point). You can read the reason furthur below.
```ts
function isolate(element: HTMLElement, isolationElement: HTMLElement) {
  if (!isolationElement) {
    isolationElement = document.createElement('div');
    isolationElement.attachShadow({mode: 'open'});
  }
  element.parentElement.insertBefore(isolationElement, element);
  element.shadowRoot.append(element);
}
```  

Code above will create `div` with `shadowRoot` and append the whole element inside the `shadowRoot`, make it isolated just in place. Then you can code your `element` as you do in Normal DOM

Next example shows an idea how to implement it in Web Component class

```js
class Component extends HTMLElement {
  isolated(element: HTMLElement) {
    if (!element) {
      element = document.createElement('div');
      element.attachShadow({ mode: 'open' });
    }
      this.parentElement.insertBefore(element, this);
      element.shadowRoot.appendChild(this);
    }
  }
}
```

---

# Why ?
**Shadow DOM** brings **"Component Isolation"** concept to Web Application. However, the way developers implements it so far is different from what I'm thinking recently.

Let's take a look at the code from https://web.dev/articles/shadowdom-v1

```js
// Use custom elements API v1 to register a new HTML tag and define its JS behavior
// using an ES6 class. Every instance of <fancy-tab> will have this same prototype.
customElements.define('fancy-tabs', class extends HTMLElement {
    constructor() {
    super(); // always call super() first in the constructor.

    // Attach a shadow root to <fancy-tabs>.
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
        <style>#tabs { ... }</style> <!-- styles are scoped to fancy-tabs! -->
        <div id="tabs">...</div>
        <div id="panels">...</div>
    `;
    }
    ...
});
```

This is the default pattern introduced by **many famous web component documentations**. It's the easiest way to jump straight to use Shadow DOM in Web Component, but it cause many following complicatations.
1. It separates DOM access between **Host** and **Shadow DOM**
2. It separated CSS application from **Host** and **Shadow DOM**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4uiup17svyqcwqz1d2jj.png)

This is where the complication start. When we use Shadom DOM like this in web component, we have to think from the start if the component will contain shadowRoot or not. Then it separate Web Component into 2 types, "Shadow" or "Light" DOM
Which also brings a lot of awkward way to implement component methods and CSS.

When you want to access child nodes from **Shadow Host**, you have to access `shadowRoot`. When you want to apply css on host node, you have to use `:host {}`. This add an extra layer to program or style component which doesn't exist in normal components.

Does it have to be like that ? On my second thought, I come up with another solution.

Let's use Shadow DOM in the right way ?
You can read my [solution](#to-the-solution) above again.