Shadow DOM
Shadow DOM is a standard that encapsulates the internal document object model (DOM) structure of a web component. Encapsulating the DOM gives developers the ability to share a component and protect the component from being manipulated by arbitrary HTML, CSS, and JavaScript. The internal DOM structure is called the shadow tree. The shadow tree affects how you work with CSS, events, and the DOM.
Since not all browsers implement Shadow DOM, LWC uses a synthetic shadow polyfill for Lightning Experience and Experience Cloud. A polyfill is code that allows a feature to work in a web browser.
To understand the
shadow tree
, let’s look at some markup. This markup contains two Lightning web components:
c-todo-app
and
c-todo-item
. The
#shadow-root
document fragment
defines the boundary between the DOM and the shadow tree. Elements below the shadow root are in the shadow tree.
Let’s look at how to work with the shadow tree in each of these areas.
CSS styles defined in a parent component don’t leak into a child. In our example, a
p
style defined in the
todoApp.css
stylesheet doesn’t style the
p
element in the
c-todo-item
component, because the styles don’t reach into the shadow tree. See
CSS
.
Events
To prevent exposing a component’s internal details, if an event bubbles up and crosses the shadow boundary, some property values change to match the scope of the listener. See
Event Retargeting
.
Access Elements
Elements in a shadow tree aren’t accessible via traditional DOM querying methods. Code can’t use
document
or
document.body
to access the shadow tree of a Lightning web component. For example, code can’t call
document.querySelector()
to select nodes in a Lightning web component’s shadow tree. To access its own shadow tree, a Lightning web component calls
this.template.querySelector()
. See
Access Elements the Component Owns
.
Access Slots
A slot is a placeholder for markup that a parent component passes into a component’s body. DOM elements that are passed to a component via slots aren’t owned by the component and aren’t in the component’s shadow tree. To access DOM elements passed via slots, call
this.querySelector()
. The component doesn't own these elements, so you don’t use
template
. See
Pass Markup into Slots
.
Test Components
See
DOM Inspection Tests Are Subject to Change
.
Don’t use these DOM APIs to reach into a component’s shadow tree in orgs that use Lightning Locker. If you’re using a third-party JavaScript library that uses these DOM APIs to reach into a component’s shadow tree, work with the library author to file and fix issues.
Document.prototype.getElementById
Document.prototype.querySelector
Document.prototype.querySelectorAll
Document.prototype.getElementsByClassName
Document.prototype.getElementsByTagName
Document.prototype.getElementsByTagNameNS
Document.prototype.getElementsByName
document.body.querySelector
document.body.querySelectorAll
document.body.getElementsByClassName
document.body.getElementsByTagName
document.body.getElementsByTagNameNS
Lightning Locker prevents you from breaking shadow DOM encapsulation between Lightning web components by blocking these APIs. However, in Aura components version 39.0 and earlier, Lightning Locker is disabled at the component level, so an Aura component could have code that fails.
These APIs aren’t restricted by
Lightning Web Security
(LWS). LWS prevents you from breaking shadow DOM encapsulation by enforcing a value of
closed
on the ShadowRoot's
mode
property for all components.
The Shadow DOM polyfill includes a patch to the
MutationObserver
interface. If you use
MutationObserver
to watch changes in a DOM tree, disconnect it or you will create a memory leak. Note that a component can observe mutations only in its own template. It can't observe mutations within the shadow tree of other custom elements.