Skip to main content Open Assistant Open Assistant ShadowDOM Widget Integration Tutorial | Engagement Tools | API Hub
Engagement Tools ShadowDOM Widget Integration Tutorial Widgets, Engagement Tools, BET
Last updated 4 months ago
Is this site helpful? # 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
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:
CSS selectors don't work - Widget container inside ShadowDOM cannot be targeted with standard selector strings, so direct DOM element must be passed instead
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) ;
When working with ShadowDOM, you cannot use CSS selector strings with SIR('addWidget'). Instead, pass a direct reference to the container DOM element.
❌ 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
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) ;
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
Critical: Set up the MutationObserver before calling the widgetloader to ensure all styles are captured during widget initialization.
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:
# 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
Both { mode: 'open' } and { mode: 'closed' } work with this approach. The widget framework doesn't need access to the shadow root after initialization.
)
;
.
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'
} ) ;
===
'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 } ) ;
>
< 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 >
Replace [CLIENT_ID] with your actual client ID and your_match_id_here with a valid match identifier.