Composition, Shadow DOM and Query Selectors in Lightning Web Components

In this post, we will talk about Composition, Shadow DOM, and Query Selectors in Lightning Web Components.

Composition in Lightning Web Components

Component Composition in LWC refers to adding of a component inside the body of another component. Composition permits you to build complex components from simple building-block components. 

Advantages of Composition

  • Composing, applications, and components from a collection of smaller components make code reusable and maintainable.
  • Reduces the code size and improves code readability

Let’s take a look at a sample Books Display Application that is composed of components. This example is used to demonstrate the concept of owner and container.

Owner

The component which owns the template. In the below example, the owner is sampleBooksDisplayApp component. This component controls all the composed components (child) that are contained in it.

Owner has the capability to

  • To set public properties on composed components
  • To call methods on composed components
  • To listen for any events triggered by composed components

Below is the code snippet that shows how Composition works in Lightning Web Components:

sampleBooksDisplayApp is parent component (owner) and it has sampleBookListCmp (child) component contained in it.

sampleBooksDisplayApp.html

<template>
    <lightning-card title="Books Display" icon-name="utility:notebook">
        <div class="slds-p-around_medium">
            <c-sample-book-list-cmp books={booksList}></c-sample-book-list-cmp>
        </div>
    </lightning-card>
</template>

sampleBooksDisplayApp.js

import { LightningElement } from 'lwc';
 
export default class SampleBooksDisplayApp extends LightningElement {
 
    booksList = ['The Alchemist' , 'The Lord of the Rings' , 'To Paradise'];
}

sampleBooksDisplayApp.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>55.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
    <target>lightning__AppPage</target>
    <target>lightning__RecordPage</target>
    <target>lightning__HomePage</target>
  </targets>  
</LightningComponentBundle>

sampleBookListCmp.html

<template>
    <div class="slds-text-heading_medium">{books}</div>
</template>

sampleBookListCmp.js

import { LightningElement, api } from 'lwc';
 
export default class SampleBookListCmp extends LightningElement {
 @api books = [];
 
}
Composition in Lightning web components

Container

 A Container contains other components but the owner component holds it.

Container has the capability to:

  • Read the public properties but not change them
  • To call methods on composed components
  • Ability to listen to few events, bubbled up by the components that it contains

Shadow DOM in Lightning web components

Shadow DOM is a mechanism that encapsulates the internal DOM (Document Object Model, known as shadow tree) of a Web Component. In Lightning Web Components, elements are encapsulated in a shadow tree.

With Shadow DOM, elements of a component have consistent behavior and styling in any context. Encapsulation provides the ability to share and protect a component from manipulation from HTML, CSS & Javascript.

Shadow tree affects the way you work with CSS, Events & DOM. The below example contains two Lightning Web Components: c-todolist-app and c-todo-task. Shadow root will define the boundary b/w DOM and Shadow tree, which is known as the Shadow boundary.

 <c-todolist-app>
        #shadow-root
        <div>
            <p>Your To Do Tasks List</p>
        </div>
        <c-todo-task>
            #shadow-root
            <div>
                <p>Go for a Walk</p>
            </div>
        </c-todo-task>
 </c-todolist-app>

Below code snippet shows how shadow DOM works

<!DOCTYPE html>
<html>
    <head>
        <style>
            div{
                font-size: 50px;
                color: darkgoldenrod;
            }
        </style>
    </head>
    <body>
       <div id="DOMExample"></div>
       <script>
           const element = document.querySelector('#DOMExample')
           const shadowRoot = element.attachShadow({mode:'open'})
           shadowRoot.innerHTML = 'Shadow DOM Example'
       </script>
    </body>
</html>

Query Selectors in the Lightning Web Component:

In order to access elements that are rendered by a component, we need to use the template property. Elements in a shadow tree cannot be accessed by traditional DOM querying methods. Use the below methods to check for the elements that a component rendered:

  • this.template.querySelector();
  • this.template.querySelectorAll();

Limitation

  1. -Order of the elements is not guaranteed
  2. -querySelector returns the results for the elements that are rendered to the DOM
  3. -it’s not a good approach to use ID selectors with querySelector. Generally, when a template is rendered, the IDs that are defined in HTML Templates are transformed into global unique values. Hence if you use an ID selector in Javascript, it doesn’t match with the transformed ID.

Below code snippet is an example for querySelector, on click of button text font and color is modified:

querySelectorSampleCmp.html

<template>
    <lightning-card title="Query Selector DEMO in LWC">
        <div class="slds-p-around_medium">
            <p>How are you doing today?</p>
        </div>
        <div class="slds-p-left_medium">
            <lightning-button label="Click Me" onclick={changeTextColor}></lightning-button>
        </div>
    </lightning-card>
</template>

querySelectorSampleCmp.js

import { LightningElement } from 'lwc';
 
export default class QuerySelectorSampleCmp extends LightningElement {
 
    changeTextColor() {
        const element = this.template.querySelector('p');
        element.style.fontSize  = '30px';
        element.style.color = 'darkgoldenrod';
    }
}

querySelectorSampleCmp.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>55.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
    <target>lightning__AppPage</target>
    <target>lightning__RecordPage</target>
    <target>lightning__HomePage</target>
  </targets>  
</LightningComponentBundle>

Output

Ravi Teja

Ravi Teja

Salesforce Consultant at Global Consulting Firm 12x Salesforce Certified Application Architect

Share this article

1 Comment

  • Thank you for sharing this. I like how you can explain complex concepts using simple examples.

Leave a reply

Subscribe for Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 4,092 other subscribers

Our Supporter

RECENT POSTS

Apex Hours

Apex Hours is one stop platform to learn Salesforce skills and technology

Join our Newsletter and get tips and tricks how to explore the salesforce for free!