DOM API Changes

Shadow DOM is a standard that encapsulates the internal document object model (DOM) structure of a web component. The internal DOM structure is called the shadow tree. In Winter ’20, code can’t use document or document.body to access the shadow tree of a Lightning web component. For example, code in a test can’t call document.querySelector() to select nodes in a Lightning web component’s shadow tree.

Why: The DOM used by HTML and JavaScript has a fundamental flaw. Unlike programming languages used for the back end, the DOM lacks encapsulation.

The Shadow DOM web standard was introduced to provide encapsulation. Encapsulating the DOM gives developers the ability to share a component and protect the component from being manipulated by arbitrary HTML, CSS, and JavaScript.

Per the Shadow DOM specification, elements in a shadow tree aren’t accessible via traditional DOM querying methods. To access its own shadow tree, a Lightning web component uses this.template.querySelector().

How: In Winter ’20, we updated Document.prototype and HTMLBodyElement.prototype to respect shadow DOM semantics. You must remove code that uses these DOM APIs to reach into a component’s shadow tree.

  • 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 already prevents you from breaking shadow DOM encapsulation between Lightning web components. 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 in Winter ’20.

Check these cases for broken code.
  • Aura components version 39.0 or earlier that use one of the DOM APIs to access the shadow tree of a Lightning web component.
  • Tests that use one of the DOM APIs to reach into the shadow tree of a Lightning web component.
  • Third-party JavaScript libraries that use one of the DOM APIs to, for example, change styles or attach event listeners in a Lightning web component.

Example

<!-- lightning:auraParent -->
<aura:component>
    <lightning:lwcParent aura:id="lwcParent"/>
</aura:component>
<!-- lightning-lwc-parent -->
<template>
    <div class="in-the-shadow"></div>
    <lightning-lwc-child></lightning-lwc-child>
</template>
<!-- lightning-lwc-child -->
<template>
    <span></span>
</template>
Inside auraBar Controller Without Locker and Before Winter ’20 Winter ’20
document.querySelector('.in-the-shadow') <div> null
document.querySelectorAll('.in-the-shadow') [<div>] (NodeList) [] (NodeList)
document.body.querySelector('.in-the-shadow') <div> null

How to Fix Aura Components

Remove code that reaches into the shadow tree of a Lightning web component. A parent component must rely on the public API of child components. For example, if a parent component needs to know the state of a checkbox in a child component, the child component exposes a public API called isChecked and the parent reads that property.

How to Fix Aura Component Tests

A component can’t reach into another component’s shadow tree, because it breaks encapsulation. However, sometimes refactoring a component to respect encapsulation isn’t possible or practical. In these test cases, we recommend using this technique.

To reach an element inside a Lightning web component’s shadow tree, first query for the Lightning web component, and then query off of its shadowRoot property.

This code accesses the <div> in the shadow tree of the <lightning-lwc-parent> component in the Example section.

var el = cmp.find('lwcParent').getElement();
var div = el.shadowRoot.querySelector('.in-the-shadow');

How to Fix Jest Tests

In a Jest test context, code can use the shadowRoot property of the element under test to access the shadow tree. The shadowRoot property encapsulates an element’s shadow tree.

This code accesses the <div> in the shadow tree of the <lightning-lwc-parent> component in the Example section.

const element = createElement('c-lightning-lwc-parent', { is: LightningLwcParent });
document.body.appendChild(element);
const div = element.shadowRoot.querySelector('.in-the-shadow'); 

How to Fix Selenium WebDriver Tests

Adapting your tests varies by tool, and strategies are rapidly evolving. Currently, this article is a good example of accessing shadow tree elements using Selenium WebDriver.

As the article discusses, global queries via WebDriver.findElement() fail. To look for an element inside a Lightning web component’s shadow tree, execute JavaScript on the client to query off the component’s shadowRoot property.

Note

Note

The article has a screenshot that shows DOM elements in Chrome Developer Tools. The screenshot shows a #shadow-root document fragment, which is the top node of a component’s shadow tree. If you look at a Lightning web component in Chrome Developer Tools, you don’t see the #shadow-root because LWC uses a shadow DOM polyfill. Salesforce supports some browsers that don’t implement the Shadow DOM web standard. The polyfill provides a shadow DOM in these browsers. To find Lightning web components on the page, look for element names that contain a hyphen. Select the element and run $0.shadowRoot in the Console. A Lightning web component returns #document-fragment.

JavaScript Libraries

If you’re using a third-party JavaScript library that reaches into a component’s shadow tree, work with the library author to file and fix issues.