W3docs

Shadow DOM Slots, Composition

Welcome to our comprehensive guide on mastering slots and composition within the JavaScript Shadow DOM. In this article, we'll explore how to leverage slots and

Welcome to our comprehensive guide on mastering slots and composition within the JavaScript Shadow DOM. The basics of the Shadow DOM are described in our previous article, Shadow DOM. In this article, we'll explore how to leverage slots and composition to enhance the flexibility, modularity, and reusability of web components. Let's dive deep into these advanced concepts and discover how they can elevate your web development skills to the next level.

Understanding Slots in Shadow DOM

Slots are placeholders within a web component's template that allow for dynamic insertion of content from the component's parent or external sources. They provide a powerful mechanism for composing customizable and reusable components. Let's explore how to effectively utilize slots in JavaScript Shadow DOM.

Defining Slots

To define a slot within a web component's template, we use the <slot> element. Slots can be default (no name attribute) or named. Let's consider a simple example with a default slot:


<body>
  <script>
    class CustomElement extends HTMLElement {
      constructor() {
        super();
        const shadowRoot = this.attachShadow({ mode: 'open' });
        shadowRoot.innerHTML = `
          <div class="container">
            <slot>Default content</slot>
          </div>
        `;
      }
    }
    
    customElements.define('custom-element', CustomElement);
    
    // Displaying the custom element
    const customElement = document.createElement('custom-element');
    document.body.appendChild(customElement);
  </script>
</body>

In this example, the <slot> element serves as a placeholder for content provided by the component's parent. Omitting the name attribute creates a default slot, which will receive any content placed directly inside the custom element without a slot attribute.

Distributing Content with Slots

Slots can receive content from the parent component or fallback content provided within the slot element itself. Let's see how slot distribution works:


<body>
  <!-- Define Custom Element -->
  <script>
    // Define Custom Element Class
    class CustomElement extends HTMLElement {
      constructor() {
        super();
        const shadowRoot = this.attachShadow({ mode: 'open' });
        shadowRoot.innerHTML = `
          <style>
            /* Define styles for the component */
            .container {
              border: 1px solid #ccc;
              padding: 20px;
            }
          </style>
          <div class="container">
            <slot name="content">Default content</slot>
          </div>
        `;
      }
    }

    // Define Custom Element
    customElements.define('custom-element', CustomElement);
  </script>

  <!-- Displaying the custom element -->
  <custom-element>
    <div slot="content">Content from parent</div>
  </custom-element>
</body>

In this example, the <div> element with slot="content" attribute will be inserted into the slot named "content" within the custom-element, overwriting the Default content.

Enhancing Composition in Shadow DOM

Composition in the context of Shadow DOM refers to assembling UI components and content by combining slots and their distributed content to create more complex, reusable structures. When applied within the context of Shadow DOM, composition enables the creation of highly customizable and reusable web components.

To style content distributed into slots from the parent, use the ::slotted() CSS pseudo-element. For example, ::slotted(div) { color: blue; } applies styles to the slotted <div>.

Composing Components with Slots

One powerful way to leverage composition in Shadow DOM is by combining components using slots. Let's create a composite component that consists of multiple child components:


<body>
  <script>
    // Define Composite Element Class
    class CompositeElement extends HTMLElement {
      constructor() {
        super();
        const shadowRoot = this.attachShadow({ mode: 'open' });
        shadowRoot.innerHTML = `
          <style>
            /* Define styles for the composite component */
            .container {
              border: 1px solid #ccc;
              padding: 20px;
            }
          </style>
          <div class="container">
            <slot name="header"></slot>
            <slot name="content"></slot>
            <slot name="footer"></slot>
          </div>
        `;
      }
    }

    // Define Composite Element
    customElements.define('composite-element', CompositeElement);
  </script>
  <composite-element>
    <div slot="header">Header</div>
    <div slot="content">Content</div>
    <div slot="footer">Footer</div>
  </composite-element>
</body>

In this example, the composite component template contains multiple slots for header, content, and footer sections. These slots can accommodate content provided by the parent component, allowing for flexible composition.

Conclusion

Mastering slots and composition in JavaScript Shadow DOM empowers developers to create highly customizable and reusable web components. By understanding and leveraging these advanced techniques effectively, you can build modular, flexible, and maintainable applications. Experiment with the provided examples, explore further possibilities, and unlock the full potential of Shadow DOM in your projects. Happy coding!

Practice

Practice

What are slots used for in JavaScript Shadow DOM?