JavaScript Shadow DOM
Der Shadow DOM ist ein zentrales Merkmal von Web Components und ermöglicht es Entwicklern, abgeschottete DOM-Bäume und Stilbereiche zu erstellen. Dieser Leitfaden soll ein tiefes Verständnis des Shadow DOM vermitteln, zusammen mit praktischen Codebeispielen, die seine Verwendung demonstrieren.
Was ist Shadow DOM?
Shadow DOM ermöglicht es Entwicklern, einen Teil des DOM und seine Stile zu kapseln und ihn vom Rest des Dokuments zu isolieren. Dadurch wird sichergestellt, dass Stile und Skripte innerhalb des Shadow DOM nicht mit denen im Hauptdokument kollidieren.
<head>
<style>
.shadow-box {
padding: 10px;
border: 1px solid #000;
background-color: lightcoral;
color: white;
}
</style>
</head>
<body>
<div class="shadow-box">This is styled by the main document</div>
<div id="host"></div>
<script>
// Create a shadow root
const hostElement = document.getElementById('host');
const shadowRoot = hostElement.attachShadow({ mode: 'open' });
// Attach shadow DOM content
shadowRoot.innerHTML = `
<style>
.shadow-box {
padding: 10px;
border: 1px solid #000;
background-color: lightblue;
color: black;
}
</style>
<div class="shadow-box">Hello, Shadow DOM!</div>
`;
</script>
</body>In diesem Beispiel gibt es zwei Elemente mit demselben Klassennamen shadow-box. Das erste Element wird durch das CSS des Hauptdokuments gestylt, während das zweite Element durch das CSS des Shadow DOM gestylt wird. Wie Sie sehen können, wirken sich die im Shadow DOM definierten Stile nicht auf die Elemente im Hauptdokument aus und umgekehrt. Dies zeigt die durch den Shadow DOM bereitgestellte Kapselung und ermöglicht es Ihnen, isolierte und wiederverwendbare Komponenten zu erstellen, ohne sich um Stilkonflikte sorgen zu müssen.
Einen Shadow Root erstellen
Um einen Shadow Root zu erstellen, verwenden Sie die Methode attachShadow an einem Element. Der Shadow Root kann entweder open oder closed sein. Ein open Shadow Root ist von JavaScript außerhalb des Shadow-Baums aus zugänglich, während ein closed Shadow Root dies nicht ist.
Offener Shadow Root
Ein offener Shadow Root ermöglicht den Zugriff und die Manipulation durch externes JavaScript. Im folgenden Beispiel ändern wir den Textinhalt innerhalb des Shadow Root, nachdem der Shadow Root erstellt wurde.
<body>
<div id="open-shadow-host"></div>
<button id="open-shadow-btn">Change Shadow Content</button>
<script>
const openShadowHost = document.getElementById('open-shadow-host');
const openShadowRoot = openShadowHost.attachShadow({ mode: 'open' });
openShadowRoot.innerHTML = `
<style>
.shadow-content {
color: blue;
padding: 10px;
border: 1px solid black;
}
</style>
<div class="shadow-content">This is an open shadow root</div>
`;
document.getElementById('open-shadow-btn').addEventListener('click', () => {
openShadowRoot.querySelector('.shadow-content').textContent = 'Open Shadow Root content updated!';
});
</script>
</body>In diesem Beispiel wird ein Button bereitgestellt, um den Inhalt des Shadow DOM zu ändern. Da der Shadow Root offen ist, können wir von dem Hauptdokument aus auf seinen Inhalt zugreifen und ihn manipulieren.
Geschlossener Shadow Root
Ein geschlossener Shadow Root schränkt den Zugriff durch externe Skripte ein und bietet dadurch eine bessere Kapselung. Im folgenden Beispiel versuchen wir, den Textinhalt innerhalb des Shadow Root zu manipulieren, nachdem der Shadow Root erstellt wurde, aber das ist nicht möglich, da er closed ist.
<body>
<div id="closed-shadow-host"></div>
<button id="closed-shadow-btn">Try to Change Shadow Content</button>
<script>
const closedShadowHost = document.getElementById('closed-shadow-host');
const closedShadowRoot = closedShadowHost.attachShadow({ mode: 'closed' });
closedShadowRoot.innerHTML = `
<style>
.shadow-content {
color: red;
padding: 10px;
border: 1px solid black;
}
</style>
<div class="shadow-content">This is a closed shadow root</div>
`;
// closedShadowHost.shadowRoot is null for closed roots, so this throws a TypeError
document.getElementById('closed-shadow-btn').addEventListener('click', () => {
try {
closedShadowHost.shadowRoot.querySelector('.shadow-content').textContent = 'Attempted to update closed shadow root!';
} catch (e) {
alert('Cannot access shadow root content from outside!');
}
});
</script>
</body>In diesem Beispiel schlägt ein Versuch fehl, auf den Inhalt des Shadow DOM zuzugreifen und ihn zu manipulieren, weil der Shadow Root geschlossen ist. Beachten Sie, dass bei geschlossenen Shadow Roots die Eigenschaft shadowRoot am Host-Element null ist, wodurch der Aufruf null.querySelector einen TypeError auslöst. Dies zeigt, wie geschlossene Shadow Roots durch eingeschränkten Zugriff eine bessere Kapselung bieten. Hinweis: Geschlossene Shadow Roots werden von allen modernen Browsern unterstützt, verbergen den Root jedoch absichtlich vor externem JavaScript, um die Kapselung durchzusetzen. Verwenden Sie sie, wenn Sie versehentlichen externen Zugriff verhindern möchten.
Styling innerhalb des Shadow DOM
WARNING
When implementing JavaScript Shadow DOM, ensure proper encapsulation to prevent unintended styling or scripting conflicts.
Stile, die innerhalb eines Shadow Root definiert werden, wirken sich nicht auf Elemente außerhalb davon aus und umgekehrt. Diese Kapselung ist nützlich, um wiederverwendbare Komponenten zu erstellen.
<head>
<style>
.styled-box {
color: red;
background-color: yellow;
padding: 10px;
border: 1px solid green;
}
</style>
</head>
<body>
<div class="styled-box">This is styled by the main document</div>
<div id="styled-host"></div>
<script>
const styledHost = document.getElementById('styled-host');
const shadowRoot = styledHost.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
.styled-box {
color: white;
background-color: black;
padding: 10px;
border-radius: 5px;
}
</style>
<div class="styled-box">Styled by Shadow DOM</div>
`;
</script>
</body>In diesem Beispiel gibt es zwei Elemente mit dem Klassennamen styled-box. Das erste Element wird durch das CSS des Hauptdokuments gestylt, während das zweite Element durch das CSS des Shadow DOM gestylt wird. Die im Shadow DOM definierten Stile wirken sich nicht auf die Elemente im Hauptdokument aus, und die im Hauptdokument definierten Stile wirken sich nicht auf die Elemente im Shadow DOM aus. Dies zeigt, wie Shadow DOM Stile kapselt und sicherstellt, dass es keine Konflikte zwischen den Stilen der Komponente und den globalen Stilen gibt.
Um Elemente innerhalb des Shadow DOM von außen zu stylen, verwenden Sie CSS Shadow Parts (::part()) und geschlitzte Pseudoelemente (::slotted()). Diese ermöglichen es externem CSS, bestimmte interne Teile oder projizierte Inhalte anzusprechen, ohne die Kapselung zu durchbrechen.
Slotting: Light-DOM-Inhalte im Shadow DOM
Slots ermöglichen es Entwicklern, Light-DOM-Inhalte (normale DOM-Inhalte) in ein Shadow DOM zu übergeben, wodurch das Shadow DOM flexibler und wiederverwendbarer wird.
<div id="slot-host">
<span slot="title">Shadow DOM Slot Example</span>
</div>
<script>
const slotHost = document.getElementById('slot-host');
const shadowRoot = slotHost.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
.container {
border: 1px solid #ccc;
padding: 10px;
}
</style>
<div class="container">
<h1><slot name="title"></slot></h1>
<p>This is a Shadow DOM component with a slot for the title.</p>
</div>
`;
</script>In diesem Beispiel wird das <slot>-Element verwendet, um Inhalte aus dem Light DOM in das Shadow DOM zu übergeben. Das slot-Attribut am span-Element stimmt mit dem name-Attribut des Slot-Elements im Shadow DOM überein, wodurch der Inhalt des Spans in das Shadow DOM projiziert werden kann.
JavaScript-Interaktion mit Shadow DOM
Die Interaktion mit dem Shadow DOM über JavaScript erfordert das Verständnis der Kapselungsgrenzen. Die direkte Manipulation innerhalb des Shadow Root ist unkompliziert, aber die externe Interaktion erfordert sorgfältige Handhabung.
Zugriff auf Shadow-DOM-Elemente
Um auf Elemente innerhalb eines Shadow DOM zuzugreifen, verwenden Sie die Eigenschaft shadowRoot.
<div id="interactive-host"></div>
<script>
const interactiveHost = document.getElementById('interactive-host');
const shadowRoot = interactiveHost.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<button id="shadow-btn">Click me</button>
`;
const shadowButton = shadowRoot.querySelector('#shadow-btn');
shadowButton.addEventListener('click', () => {
alert('Button inside Shadow DOM clicked!');
});
</script>In diesem Beispiel greifen wir mit querySelector auf dem Shadow Root auf den Button innerhalb des Shadow DOM zu. Da der Shadow Root offen ist, können wir Ereignis-Listener anhängen und Elemente direkt vom Hauptdokument aus manipulieren.
Praktische Beispiele für Shadow DOM
Erstellen einer wiederverwendbaren Web-Komponente
Das Erstellen einer wiederverwendbaren Web-Komponente mit Shadow DOM umfasst das Definieren eines benutzerdefinierten Elements und das Anhängen eines Shadow Root daran.
<body>
<custom-card title="Hello World"></custom-card>
<script>
class CustomCard extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
.card {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.card-title {
font-size: 1.2em;
margin-bottom: 5px;
}
</style>
<div class="card">
<div class="card-title">${this.getAttribute('title')}</div>
<div class="card-content"><slot></slot></div>
</div>
`;
}
}
customElements.define('custom-card', CustomCard);
</script>
</body>In diesem Beispiel wird ein benutzerdefiniertes Element <custom-card> mit einem Shadow DOM erstellt. Der Shadow DOM kapselt die Stile und die Struktur der Komponente und macht sie wiederverwendbar, ohne sich um Stilkonflikte mit dem Hauptdokument sorgen zu müssen.
Integration mit Frameworks
Shadow DOM kann nahtlos mit modernen JavaScript-Frameworks wie React, Angular und Vue verwendet werden.
React-Beispiel
In React können Sie wie folgt einen Shadow DOM an ein Container-Element anhängen:
<body>
<div id="root"></div>
<!-- React and ReactDOM CDN links -->
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const { useRef, useLayoutEffect } = React;
const CustomCard = ({ title, content }) => {
const cardRef = useRef(null);
useLayoutEffect(() => {
if (cardRef.current) {
const shadowRoot = cardRef.current.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
.card {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.card-title {
font-size: 1.2em;
margin-bottom: 5px;
}
</style>
<div class="card">
<div class="card-title">${title}</div>
<div class="card-content">${content}</div>
</div>
`;
}
}, [title, content]);
return <div ref={cardRef}></div>;
};
const App = () => (
<CustomCard title="Hello World" content="This is content inside the shadow DOM.">
</CustomCard>
);
const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
root.render(<App />);
</script>
</body>In diesem Beispiel wird eine React-Komponente CustomCard erstellt, die einen Shadow DOM an ein normales div anhängt. Der Shadow DOM stellt sicher, dass die Stile und die Struktur der Komponente gekapselt sind, und ermöglicht eine nahtlose Integration mit React.
Fazit
Das Beherrschen von Shadow DOM ist für die moderne Webentwicklung unerlässlich, da es leistungsstarke Kapselung und Wiederverwendbarkeit bietet. Wenn Sie die bereitgestellten Konzepte und Beispiele verstehen und umsetzen, können Sie robuste, isolierte Komponenten erstellen, die die Wartbarkeit und Skalierbarkeit Ihrer Webanwendungen verbessern.
Dieser umfassende Leitfaden sollte als solide Grundlage dienen, um Shadow DOM in Ihren Projekten zu erkunden und zu nutzen. Ganz gleich, ob Sie einfache Widgets oder komplexe Anwendungen erstellen, Shadow DOM bietet die Kapselung und Flexibilität, die erforderlich sind, damit Ihre Komponenten isoliert und gut verwaltbar bleiben.
Practice
Which method is used to create a shadow root in JavaScript?