Open vs Closed Modes
📅
Two Operation Modes
Shadow DOM supports two access modes:
1. Open Mode
External access possible via element.shadowRoot:
// Creation
const element = document.createElement('div');
element.attachShadow({ mode: 'open' });
// External access
console.log(element.shadowRoot); // Accessible
element.shadowRoot.innerHTML = '<p>Content</p>';
2. Closed Mode
External access impossible, element.shadowRoot returns null:
// Creation
const element = document.createElement('div');
element.attachShadow({ mode: 'closed' });
// External access
console.log(element.shadowRoot); // null
Mode Comparison
| Characteristic | Open Mode | Closed Mode |
|---|---|---|
| JavaScript Access | ✅ Via element.shadowRoot |
❌ Always null |
| Security | 🔓 Lower | 🔒 Higher |
| Flexibility | ✅ High | ❌ Limited |
| Usage | 95% cases | 5% special cases |
Practical Examples
Open Mode (recommended)
class OpenComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>:host { display: block; }</style>
<p>Open component</p>
`;
}
}
// External access
const comp = document.querySelector('open-component');
comp.shadowRoot.querySelector('p').style.color = 'red';
Closed Mode (rare cases)
class ClosedComponent extends HTMLElement {
constructor() {
super();
this._shadowRoot = this.attachShadow({ mode: 'closed' });
this._shadowRoot.innerHTML = `
<style>:host { display: block; }</style>
<p>Closed component</p>
`;
}
// Special access method
getShadowRoot() {
return this._shadowRoot;
}
}
// Access only via API
const comp = document.querySelector('closed-component');
comp.getShadowRoot().querySelector('p').style.color = 'blue';
When to Use Each
✅ Use Open Mode for:
- Most UI components
- Libraries and frameworks
- Components requiring customization
- Situations needing internal access
⚠️ Use Closed Mode for:
- Critical components
- Internal logic protection
- Components with sensitive data
- Strict API interfaces
Closed Mode Features
Access Restriction
class SecureComponent extends HTMLElement {
constructor() {
super();
this._shadow = this.attachShadow({ mode: 'closed' });
this._shadow.innerHTML = `<p>Secret content</p>`;
}
// Controlled API
updateContent(text) {
this._shadow.querySelector('p').textContent = text;
}
}
// No direct access
const secure = document.querySelector('secure-component');
secure.shadowRoot; // null
secure.updateContent('New text'); // Via API
Bypassing Restrictions (not recommended)
// Encapsulation violation (anti-pattern!)
const element = document.querySelector('closed-component');
const shadow = element._shadowRoot; // Access private field
Declarative Approach
Open Mode
<my-element>
<template shadowrootmode="open">
<p>Open declarative Shadow DOM</p>
</template>
</my-element>
Closed Mode
<my-element>
<template shadowrootmode="closed">
<p>Closed declarative Shadow DOM</p>
</template>
</my-element>
Best Practices
- Use open mode by default
- Closed mode only for special cases
- Don't rely on private fields for bypassing
- Document API for closed components
- Test both modes in target browsers
Browser Support
Both modes supported in all modern browsers:
- Chrome 53+ ✅
- Firefox 63+ ✅
- Safari 10.1+ ✅
- Edge 79+ ✅