Shadow DOM
Web Components: Shadow DOM
What is the Shadow DOM?
View Answer:
What are the benefits of using Shadow DOM?
View Answer:
How is a Shadow DOM tree attached to an element?
View Answer:
HTML:
<div id="shadowHost"> </div>
JavaScript:
// Select the host element
let shadowHost = document.getElementById('shadowHost');
// Attach the shadow root
let shadowRoot = shadowHost.attachShadow({mode: 'open'});
// Now we can populate the shadow DOM
let content = document.createElement('p');
content.textContent = 'This is content in the Shadow DOM!';
shadowRoot.appendChild(content);
What is "open" and "closed" mode in Shadow DOM?
View Answer:
Here's an example to demonstrate the difference between "open" and "closed" Shadow DOM:
// Open mode
let openElem = document.createElement('div');
let openShadow = openElem.attachShadow({mode: 'open'});
console.log(openElem.shadowRoot); // Logs the Shadow root
// Closed mode
let closedElem = document.createElement('div');
let closedShadow = closedElem.attachShadow({mode: 'closed'});
console.log(closedElem.shadowRoot); // Logs null
In the above example, openElem.shadowRoot
gives us the shadow root because it's in "open" mode, but closedElem.shadowRoot
returns null because it's in "closed" mode.
Can styles penetrate Shadow DOM?
View Answer:
Let's consider a custom element with Shadow DOM:
HTML:
<my-element></my-element>
JavaScript:
class MyElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'}).innerHTML = `
<style>
p {
color: var(--color, black);
}
</style>
<p>Hello!</p>
`;
}
}
customElements.define('my-element', MyElement);
CSS:
my-element {
--color: red;
}
Here, my-element
contains a Shadow DOM with a p
tag. Although styles from the main document don't penetrate the Shadow DOM, CSS custom properties can. The CSS variable --color
is defined in the main document and used in the Shadow DOM, turning the text red.
What is a "light DOM"?
View Answer:
Here's an example showing a custom element with light DOM content:
HTML:
<my-element>
<p>This is light DOM content!</p>
</my-element>
JavaScript:
class MyElement extends HTMLElement {
constructor() {
super();
let shadow = this.attachShadow({mode: 'open'});
let slot = document.createElement('slot');
shadow.appendChild(slot); // slot element is a placeholder for light DOM content
}
}
customElements.define('my-element', MyElement);
In this example, the paragraph (<p>This is light DOM content!</p>
) is the light DOM content for my-element
. The 'slot' element is used in the Shadow DOM to designate a place for this light DOM content.
How are complex browser controls parsed into the browser?
View Answer:
What is a slot in relation to the Shadow DOM?
View Answer:
Here's an example showing a custom element with a slot in its Shadow DOM:
HTML:
<my-element>
<p>This is light DOM content!</p>
</my-element>
JavaScript:
class MyElement extends HTMLElement {
constructor() {
super();
let shadow = this.attachShadow({mode: 'open'});
let slot = document.createElement('slot');
shadow.appendChild(slot); // slot element is a placeholder for light DOM content
}
}
customElements.define('my-element', MyElement);
In this example, the 'slot' element in the Shadow DOM of my-element
serves as a placeholder for the Light DOM content (<p>This is light DOM content!</p>
). When my-element
is rendered, the slot is replaced by the Light DOM content.
How do events behave in Shadow DOM?
View Answer:
Here's an example showing how events behave in a Shadow DOM:
HTML:
<my-button></my-button>
JavaScript:
class MyButton extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'}).innerHTML = `<button>Click me</button>`;
}
connectedCallback() {
this.shadowRoot.querySelector('button').addEventListener('click', () => {
console.log('Button clicked inside Shadow DOM');
});
}
}
customElements.define('my-button', MyButton);
document.querySelector('my-button').addEventListener('click', (event) => {
console.log('Event received in light DOM, target:', event.target);
});
When the button inside my-button
's Shadow DOM is clicked, it logs 'Button clicked inside Shadow DOM'. The same click event propagates to the main document, triggering the 'click' event listener on my-button
, but the event target is retargeted to my-button
.
Can JavaScript interact with elements inside a Shadow DOM?
View Answer:
Here's a JavaScript code example of interacting with elements inside a Shadow DOM:
// Assume a custom element with an attached shadow root
let customElement = document.querySelector('my-element');
let shadowRoot = customElement.shadowRoot; // Access the shadow root
let shadowElement = shadowRoot.querySelector('.shadow-class'); // Access an element in the shadow DOM
shadowElement.textContent = 'New Text'; // Change the content of the element
This code assumes there's a custom element <my-element>
in the document with an attached Shadow DOM, and inside the Shadow DOM, there's an element with the class shadow-class
. The script accesses the Shadow DOM, finds the element, and changes its text content.
How do you distribute nodes in Shadow DOM?
View Answer:
Can you access elements in a closed Shadow DOM?
View Answer:
What is event retargeting in Shadow DOM?
View Answer:
Here's an example illustrating event retargeting in Shadow DOM:
HTML:
<my-element>
<button>Click me</button>
</my-element>
JavaScript:
class MyElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' }).innerHTML = `
<button>Button inside Shadow DOM</button>
`;
}
}
customElements.define('my-element', MyElement);
document.querySelector('my-element').addEventListener('click', (event) => {
console.log('Event received in light DOM, target:', event.target);
});
When you click the button inside my-element
's Shadow DOM, the event bubbles out to the light DOM and triggers the 'click' event listener. The target
property of the event is retargeted to the host element (my-element
), and it logs the target as the host element in the console.
Are Shadow DOM elements accessible via JavaScript calls or selectors?
View Answer:
Here's a basic JavaScript code example of accessing a Shadow DOM element:
let hostElement = document.querySelector('#shadow-host');
let shadowRoot = hostElement.shadowRoot; // Access the shadow root
let shadowElement = shadowRoot.querySelector('.shadow-element'); // Access element within the shadow DOM
This assumes an element with the id shadow-host
contains a Shadow DOM, and there's an element with the class shadow-element
within that Shadow DOM.
Regarding the DOM, what are the two types of subtrees?
View Answer:
Is it possible to hide custom element component internals using the shadow DOM?
View Answer:
<script>
customElements.define(
'show-hello',
class extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
`;
}
}
);
</script>
<show-hello name="John"></show-hello> // shows Hello, John
What are the limitations to creating a shadow root on a custom element?
View Answer:
Can you explain the function of the attachShadow() method?
View Answer:
Syntax: const shadow = this.attachShadow({mode: 'open'});
Here's a JavaScript code example of using attachShadow()
to create a shadow root:
// Create a new custom element
let customElement = document.createElement('div');
// Attach a shadow root to the custom element
let shadow = customElement.attachShadow({mode: 'open'});
// Now you can add content to the shadow root
shadow.innerHTML = '<p>Hello from the shadow DOM!</p>';
In this example, the attachShadow
method is used to create a new shadow root for the customElement
. The content of the shadow root is then set to a paragraph with the text "Hello from the shadow DOM!".
You cannot attach a shadow root to every type of element, and some cannot have a shadow DOM for security reasons (for example, <a>
) and more besides.
Can you explain the delimitation of the shadow DOM from the main document?
View Answer:
<style>
/* document style won't apply to the shadow tree inside #elem (1) */
p {
color: red;
}
</style>
<div id="elem"></div>
<script>
elem.attachShadow({ mode: 'open' });
// shadow tree has its own style (2)
elem.shadowRoot.innerHTML = `
`;
// <p> is only visible from queries inside the shadow tree (3)
console.log(document.querySelectorAll('p').length); // 0
console.log(elem.shadowRoot.querySelectorAll('p').length); // 1
</script>