JavaScript Shadow DOM

JavaScript Shadow DOM

JavaScript Shadow DOM

The Shadow DOM is a key feature of Web Components that allows you to encapsulate the internal structure and style of a component, preventing external styles from affecting it and vice versa. This provides true modularity and reusability, as it allows custom elements to have their own independent DOM tree, with styles and behavior that don't interfere with the main document.

With the Shadow DOM, you can create self-contained components that behave as though they have their own "hidden" DOM, separate from the document’s main DOM. This isolation also allows for easier style scoping and avoids global style conflicts.

What is Shadow DOM?

  • The Shadow DOM creates a separate, isolated DOM for an element, allowing it to have its own HTML structure and CSS, which doesn't affect or get affected by the surrounding page's styles.
  • It ensures that internal implementation details (markup, styles, and JavaScript) are encapsulated from the global scope.

Key Features of Shadow DOM

  1. Encapsulation: The component's structure and styles are isolated.
  2. Style Scoping: Styles inside the shadow tree are scoped only to that tree, so they won't affect the rest of the document.
  3. Modularity: Components can be reused without worrying about style or behavior conflicts with other parts of the page.
  4. Lifecycle: The shadow DOM is a part of the lifecycle of custom elements, allowing you to hook into different lifecycle stages.

How to Use Shadow DOM in JavaScript

To use Shadow DOM, you need to attach it to an element by calling attachShadow() on an HTML element. This method creates a shadow root, which you can use to insert your own HTML and CSS.

Basic Steps:

  1. Create a custom element or any existing HTML element.
  2. Use the attachShadow() method to create a shadow DOM.
  3. Add content (HTML, CSS, etc.) inside the shadow DOM.

Example: Creating a Shadow DOM

Here’s an example that demonstrates how to create a simple custom element with a Shadow DOM:

Step 1: Create the Custom Element Class

class MyShadowElement extends HTMLElement { constructor() { super(); // Always call super() first // Attach shadow DOM to this element (mode can be 'open' or 'closed') this.attachShadow({ mode: 'open' }); // Adding HTML content inside the shadow DOM this.shadowRoot.innerHTML = ` <style> p { color: green; font-size: 18px; } </style> <p>This is content inside the shadow DOM!</p> `; } // Optionally, add lifecycle methods like connectedCallback connectedCallback() { console.log('Custom element with shadow DOM is added to the page'); } } // Define the custom element customElements.define('my-shadow-element', MyShadowElement);

Step 2: Use the Custom Element

<my-shadow-element></my-shadow-element>

In this example:

  • this.attachShadow({ mode: 'open' }): Attaches a shadow root to the element. The mode: 'open' means that the shadow DOM is accessible via JavaScript through element.shadowRoot. If it was 'closed', the shadow DOM would not be accessible.
  • this.shadowRoot.innerHTML: You can insert HTML and CSS into the shadow DOM. These styles are scoped to the shadow tree and will not affect other parts of the document.

Shadow DOM with Open and Closed Modes

When creating a shadow root, you can specify the mode as either open or closed.

  1. mode: 'open':

    • Allows direct access to the shadow DOM through the shadowRoot property.
    • Can be accessed using element.shadowRoot.
    • Example:
      let shadowRoot = element.shadowRoot;
  2. mode: 'closed':

    • Prevents direct access to the shadow DOM via the shadowRoot property.
    • Shadow DOM can only be manipulated from within the component's code, and no direct access is allowed from outside.
    • Example:
      // No access to shadowRoot from the outside when mode is 'closed' let shadowRoot = element.shadowRoot; // This will return null

Advanced Example: Using Shadow DOM with External Styles

You can combine Shadow DOM with external styles to create more complex components.

Step 1: Define the Shadow Element

class CardComponent extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); // Adding external CSS and content to shadow DOM this.shadowRoot.innerHTML = ` <link rel="stylesheet" href="styles.css"> <div class="card"> <h2></h2> <p></p> </div> `; } connectedCallback() { // Setting text content of the card from element attributes const title = this.getAttribute('title') || 'Default Title'; const description = this.getAttribute('description') || 'Default description.'; this.shadowRoot.querySelector('h2').textContent = title; this.shadowRoot.querySelector('p').textContent = description; } } customElements.define('card-component', CardComponent);

Step 2: Usage

<card-component title="Shadow DOM Card" description="This is a shadow DOM example."></card-component>

In this example:

  • The shadow DOM contains a reference to an external stylesheet (styles.css), which allows you to define styles for your component without affecting the rest of the document.
  • The component is initialized using the connectedCallback() method, where the title and description attributes are set dynamically.

Styling with Shadow DOM

The styles defined within the Shadow DOM are encapsulated, meaning they only apply to the elements inside the shadow tree. Styles outside of the shadow DOM cannot affect it, and styles inside the shadow DOM will not leak out.

Scoped Styles Example:

this.shadowRoot.innerHTML = ` <style> p { color: red; font-size: 20px; } </style> <p>This paragraph is inside the shadow DOM and has scoped styles.</p> `;

Here, the <p> element inside the shadow DOM is styled independently of the global page styles.

External Styles Impact

  • Styles applied outside the shadow DOM will not affect the shadow DOM elements.
  • Styles inside the shadow DOM will not leak out and affect the global document styles.

Event Handling in Shadow DOM

Events in the Shadow DOM are isolated, meaning that events fired inside the shadow DOM do not bubble up to the main document unless explicitly allowed.

If you want events in the shadow DOM to propagate to the parent document, you can use the composed option when dispatching events.

Example: Dispatching Events from Shadow DOM

class ButtonWithEvent extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.innerHTML = ` <button>Click Me</button> `; this.shadowRoot.querySelector('button').addEventListener('click', () => { const event = new CustomEvent('my-button-clicked', { bubbles: true, composed: true }); this.dispatchEvent(event); }); } } customElements.define('button-with-event', ButtonWithEvent);
<button-with-event></button-with-event> <script> document.querySelector('button-with-event').addEventListener('my-button-clicked', () => { alert('Button inside Shadow DOM was clicked!'); }); </script>

In this example, the click event inside the shadow DOM bubbles up to the main document because of the { bubbles: true, composed: true } options when dispatching the CustomEvent.

Summary of Shadow DOM

  • Encapsulation: The Shadow DOM allows for self-contained components with their own DOM structure, styles, and behaviors, isolated from the rest of the page.
  • Shadow DOM Modes: Use 'open' for accessible shadow DOM or 'closed' for hidden shadow DOM.
  • Style Isolation: Styles inside the shadow DOM don't affect the global document, and vice versa.
  • Event Handling: Events in the Shadow DOM do not bubble to the outside unless explicitly configured.

The Shadow DOM, when combined with other Web Component technologies like Custom Elements and HTML Templates, allows developers to create modular, reusable, and isolated components for modern web applications.

Soeng Souy

Soeng Souy

Website that learns and reads, PHP, Framework Laravel, How to and download Admin template sample source code free.

Post a Comment

CAN FEEDBACK
close