Skip to main content
Logo
Explore APIsContact Us
  • Home
  • Match Preview
  • Tournament Preview
  • Virtual Stadium
  1. Resources
  2. Widgets
  3. ShadowDOM Widget Integration Tutorial

ShadowDOM Widget Integration Tutorial

#Intended Audience

This tutorial is for:

  • Advanced frontend developers working with Web Components and ShadowDOM
  • Technical integrators building encapsulated component architectures
  • Developers familiar with JavaScript DOM manipulation and the ShadowDOM API
  • Those who have completed basic widget integration and need ShadowDOM support

#Goals

By completing this tutorial, you will:

  • Understand how to integrate widgets inside ShadowDOM containers
  • Learn to pass DOM element references instead of CSS selectors
  • Implement a MutationObserver to clone widget styles into ShadowDOM
  • Create a complete, working ShadowDOM widget integration
  • Handle both open and closed ShadowDOM modes

#Prerequisites

Before starting this tutorial, ensure you have:

  • Valid Sportradar Widget license - Contact Sales if needed
  • Client ID - Provided with your license
  • JavaScript knowledge - Understanding of:
    • DOM manipulation
    • ShadowDOM API (attachShadow, shadow roots)
    • MutationObserver API
    • ES6+ syntax
  • Basic widget integration experience - Completed Getting Started Guide
  • Web development environment - Text editor and modern browser with ShadowDOM support (Chrome, Firefox, Safari, or Edge)
  • Match ID - A valid match identifier for testing
info

ShadowDOM is supported in all modern browsers. Legacy browsers (IE11) do not support ShadowDOM and require polyfills.


#Overview

ShadowDOM provides style and DOM encapsulation for web components, but this encapsulation presents two challenges for widget integration:

  1. CSS selectors don't work - Widget container inside ShadowDOM cannot be targeted with standard selector strings, so direct DOM element must be passed instead
  2. Styles are isolated - Widget styles injected into <head> are not visible inside ShadowDOM, so they need to be cloned into the ShadowDOM.

This tutorial shows you how to overcome both challenges.


#Tutorial Steps

#Step 1: Create ShadowDOM Container

First, create a host element and attach a shadow root to it. Then create a container element inside the shadow root where the widget will be rendered.

html
<div id="widget-host"></div>
js
const host = document.getElementById('widget-host');
const shadowRoot = host.attachShadow({ mode: 'open' });
const widgetContainer = document.createElement('div');
shadowRoot.appendChild(widgetContainer);
tip

Both { mode: 'open' } and { mode: 'closed' } work with this approach. The widget framework doesn't need access to the shadow root after initialization.

#Step 2: Pass DOM Element Reference to Widget API

When working with ShadowDOM, you cannot use CSS selector strings with SIR('addWidget'). Instead, pass a direct reference to the container DOM element.

warning

❌ This will NOT work:

js
SIR('addWidget', '#widget-container', 'match.lmtPlus', { ... });

✅ This WILL work:

js
SIR('addWidget', widgetContainer, 'match.lmtPlus', { ... });

Example initialization:

js
// Include widgetloader
(function(a,b,c,d,e,f,g,h,i){a[e]||(i=a[e]=function(){(a[e].q=a[e].q||[]).push(arguments)},i.l=1*new Date,i.o=f,
g=b.createElement(c),h=b.getElementsByTagName(c)[0],g.async=1,g.src=d,g.setAttribute("n",e),h.parentNode.insertBefore(g,h)
)})(window,document,"script","https://widgets.sir.sportradar.com/[CLIENT_ID]/widgetloader","SIR",{
    theme: 'default',
    language: 'en'
});

SIR('addWidget', widgetContainer, 'match.lmtPlus', {
    matchId: 'your_match_id_here'
});
note

Replace [CLIENT_ID] with your actual client ID and your_match_id_here with a valid match identifier.

#Step 3: Set up Style Container

Create a dedicated container inside the ShadowDOM to hold the cloned <link> elements.

js
const widgetContainer = document.createElement('div');
const linkContainer = document.createElement('div');
shadowRoot.appendChild(linkContainer);
shadowRoot.appendChild(widgetContainer);
info

The linkContainer should be added before the widgetContainer to ensure styles are loaded in the correct order.

#Step 4: Implement MutationObserver for Style Cloning

Widgets inject <link> and <style> elements into the document <head>. These styles are not visible inside ShadowDOM by default. Use a MutationObserver to detect and clone these elements.

#Understanding What to Clone

Only clone widget-specific styles to avoid conflicts:

  • <link> elements with href containing widgets.sir.sportradar.com
  • <style> element with id="sr-client-theme" (only when using custom themes)
js
const observer = new MutationObserver(mutations => {
    mutations.forEach(mutation => {
        mutation.addedNodes.forEach(node => {
            // Clone widget stylesheet links
            if (node.tagName === 'LINK' &&
                node.rel === 'stylesheet' &&
                node.href.includes('https://widgets.sir.sportradar.com')) {
                linkContainer.appendChild(node.cloneNode());
            }

            // Clone client theme styles (only if using custom theme)
            if (node.tagName === "STYLE" && node.id === "sr-client-theme") {
                const clonedNode = node.cloneNode(true);
                clonedNode.id = "sr-client-theme-clone";
                // Append after linkContainer to maintain style precedence
                shadowRoot.appendChild(clonedNode);
            }
        });
    });
});

observer.observe(document.head, { childList: true });
warning

Critical: Set up the MutationObserver before calling the widgetloader to ensure all styles are captured during widget initialization.

#Step 5: Initialize Widget

Now that the observer is in place, initialize the widget as shown in Step 2. The observer will automatically clone styles as they are added to the document.

js
SIR('addWidget', widgetContainer, 'match.lmtPlus', {
    matchId: 'your_match_id_here'
});

#Complete Implementation

View Complete Working Example

Here's the full implementation combining all steps:

html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>ShadowDOM Widget Integration</title>
</head>
<body>
    <div id="widget-host"></div>

    <script>
        // Step 1: Create ShadowDOM container
        const host = document.getElementById('widget-host');
        const shadowRoot = host.attachShadow({ mode: 'open' });

        // Step 3: Create containers for styles and widget
        const linkContainer = document.createElement('div');
        const widgetContainer = document.createElement('div');
        shadowRoot.appendChild(linkContainer);
        shadowRoot.appendChild(widgetContainer);

        // Step 4: Set up MutationObserver (BEFORE widgetloader)
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    // Clone widget stylesheet links
                    if (node.tagName === 'LINK' &&
                        node.rel === 'stylesheet' &&
                        node.href.includes('https://widgets.sir.sportradar.com')) {
                        linkContainer.appendChild(node.cloneNode());
                    }

                    // Clone client theme styles
                    if (node.tagName === "STYLE" && node.id === "sr-client-theme") {
                        const clonedNode = node.cloneNode(true);
                        clonedNode.id = "sr-client-theme-clone";
                        shadowRoot.appendChild(clonedNode);
                    }
                });
            });
        });

        observer.observe(document.head, { childList: true });

        // Step 2 & 5: Initialize widgetloader and add widget
        (function(a,b,c,d,e,f,g,h,i){a[e]||(i=a[e]=function(){(a[e].q=a[e].q||[]).push(arguments)},i.l=1*new Date,i.o=f,
        g=b.createElement(c),h=b.getElementsByTagName(c)[0],g.async=1,g.src=d,g.setAttribute("n",e),h.parentNode.insertBefore(g,h)
        )})(window,document,"script","https://widgets.sir.sportradar.com/[CLIENT_ID]/widgetloader","SIR",{
            theme: 'default',
            language: 'en'
        });

        // Pass DOM element reference (not selector string)
        SIR('addWidget', widgetContainer, 'match.lmtPlus', {
            matchId: 'your_match_id_here'
        });
    </script>
</body>
</html>
note

Replace [CLIENT_ID] with your actual client ID and your_match_id_here with a valid match identifier.


#Key Takeaways

DOM Element References

Always pass a direct DOM element reference to SIR('addWidget') when using ShadowDOM. CSS selector strings cannot penetrate shadow boundaries.

Style Cloning

Use MutationObserver to clone widget-specific <link> and <style> elements from <head> into the ShadowDOM container.

Order Matters

Set up the MutationObserver before loading the widgetloader script to capture all style elements during initialization.


#Further Reading

#Sportradar Widget Documentation

  • Getting Started Guide - Basic widget integration
  • Global SIR API - Complete API reference
  • Widget Customization - Theming and styling options

#Web Platform APIs

  • ShadowDOM API (MDN) - Comprehensive guide to ShadowDOM
  • MutationObserver API (MDN) - Observing DOM changes
  • Web Components (MDN) - Building encapsulated components
Last updated 14 days ago
Is this site helpful?
Widgets, Engagement Tools
Customizing Widget TabsUS Sports Integration Tutorial
On this page
  • Intended Audience
  • Goals
  • Prerequisites
  • Overview
  • Tutorial Steps
  • Step 1: Create ShadowDOM Container
  • Step 2: Pass DOM Element Reference to Widget API
  • Step 3: Set up Style Container
  • Step 4: Implement MutationObserver for Style Cloning
  • Step 5: Initialize Widget
  • Complete Implementation
  • Key Takeaways
  • Further Reading
  • Sportradar Widget Documentation
  • Web Platform APIs