Embed Portal is a lightweight custom element that allows you to fetch external HTML and inject it directly into a Shadow DOM.
While it functions similarly to an <iframe>, using a Shadow DOM keeps the embedded content encapsulated within the light DOM's styling and structure without the overhead or isolation constraints of a full iframe.
- Encapsulation: Styles defined in the parent document won't leak into the portal (and vice versa).
- Performance: Avoids the heavy resource usage associated with loading multiple independent browser contexts (iframes).
- Seamless Integration: Allows you to fetch and display modular components or partial pages dynamically.
Add this script to your project:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tkdeng/embed-portal/style.min.css"/>
<script src="https://cdn.jsdelivr.net/gh/tkdeng/embed-portal/script.min.js" defer></script>Simply use the <embed-portal> tag in your HTML:
<embed-portal src="/embed-shadow-dom"></embed-portal>You can react to the portal's lifecycle using standard event listeners:
const portal = document.querySelector('embed-portal');
// Handle successful load
portal.addEventListener('load', (event) => {
console.log('Portal content injected successfully!');
// Access the shadow root directly from the event
const shadowRoot = event.detail.root;
});
// Handle errors
portal.addEventListener('error', (event) => {
console.error('Failed to load portal:', event.detail.message);
});| Attribute | Description |
|---|---|
| src | The URL of the HTML content to embed (must be same-origin). |
| noscript | Optional. If present, prevents the script from attempting to extract and execute <script> tags from the fetched content. |
| onload | Optional. A string containing JavaScript to execute once the content has been successfully injected. |
- Polling: The script periodically scans the DOM for new
<embed-portal>elements. - Fetching: It performs a GET request to the provided src with a custom X-Fetch-Dest: embed-portal header.
- Parsing: It extracts
<link>stylesheets and<style>blocks from the document head and injects them into the Shadow DOM to preserve styling. - Injection: It extracts the
<body>content and appends it into the shadow root. - Events: Once loaded, it dispatches a custom
loadevent containing theshadowroot in thedetailobject (orerrorif the request fails).
- Security: This implementation enforces same-origin requests to prevent CORS issues. Invalid requests trigger an
errorevent and log a sanitized URL to the console. - Script Handling: By default, it attempts to extract and register scripts from the fetched page to the main document head. You can disable this with the
noscriptattribute.