This tutorial is for:
By completing this tutorial, you will:
onTrack callback to detect successful widget initializationBefore starting this tutorial, ensure you have:
onTrack callbackThis tutorial builds on the concepts from the Adding Loading Indicators tutorial. If you're new to onTrack callbacks, review that tutorial first.
In many UX scenarios, you want to show UI controls (buttons, tabs, toggles) only when the associated widget loads successfully:
Using the silent error mode hides errors but doesn't help you conditionally display UI elements. You need programmatic error detection.
Load widgets in a hidden container, check for successful initialization using onTrack, then move them to the visible area and display related controls only if no errors occurred.
Create two separate areas: a hidden loading area for widgets, and a visible content area for UI controls.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Conditional Widget Display</title>
<style>
#your-content {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
</style>
</head>
<body>
<!-- Visible content area -->
<div id="your-content">
<!-- Button hidden initially, shown only if widget loads successfully -->
<button id="toggle" style="display: none">Toggle widget display</button>
</div>
<!-- Hidden loading area for widgets -->
<div id="loading-area" style="display: none">
<div id="widget" class="sr-widget" style="display: none"></div>
</div>
</body>
</html>#your-content - Contains UI controls that should only appear when the widget loads successfully. Initially, the button is hidden.
#loading-area - Hidden container where widgets load. This prevents users from seeing widgets during initialization or if errors occur.
#widget - The actual widget element, initially hidden with display: none within the hidden loading area.
This double-hiding approach (loading area hidden AND widget hidden) gives you fine-grained control over when and where the widget appears.
Obtain references to all HTML elements you'll need to manipulate.
// Get the HTML elements from the document
const loadingArea = document.getElementById('loading-area');
const pageContent = document.getElementById('your-content');
const widget = document.getElementById('widget');
const button = document.getElementById('toggle');Load the widget framework with your configuration.
(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/betradar/widgetloader", "SIR", {
theme: false,
language: "en"
});Create a callback that listens for successful widget loading and moves the widget to the visible area.
// Function to listen to widget events
const onTrack = (eventType, data) => {
if (eventType === 'data_change') {
if (!data.error) {
// Widget loaded successfully - move it to visible area
loadingArea.removeChild(widget);
pageContent.appendChild(widget);
button.style.display = 'block';
}
// If data.error exists, widget stays hidden and button stays hidden
}
}Understanding the Logic Flow
data_change event - Fired when widget receives data (successful or error)!data.error means no errors occurredThe widget continues to exist in the hidden loading area if an error occurs. Users never see broken widgets or empty spaces.
Create a function to toggle widget visibility when the button is clicked.
// Toggle widget visibility on button click
const onWidgetToggle = () => {
widget.style.display = widget.style.display === 'none' ? 'block' : 'none';
}
button.onclick = onWidgetToggle;This toggle function only becomes accessible because the button is only displayed when the widget loaded successfully.
Initialize the widget with the onTrack callback.
SIR("addWidget", "#widget", "match.scoreboard", {
matchId: 43406689,
onTrack: onTrack
});View Complete Working Example
<!DOCTYPE html>
<html lang="en">
<head>
<title>Conditional Widget Display</title>
<style>
#your-content {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
</style>
</head>
<body>
<div id="your-content">
<button id="toggle" style="display: none">Toggle widget display</button>
</div>
<!-- Load widgets in hidden area -->
<div id="loading-area" style="display: none">
<div id="widget" class="sr-widget" style="display: none"></div>
</div>
<script>
// Get DOM element references
const loadingArea = document.getElementById('loading-area');
const pageContent = document.getElementById('your-content');
const widget = document.getElementById('widget');
const button = document.getElementById('toggle');
// Initialize 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/betradar/widgetloader", "SIR", {
theme: false,
language: "en"
});
// onTrack callback with error detection
const onTrack = (eventType, data) => {
if (eventType === 'data_change') {
if (!data.error) {
loadingArea.removeChild(widget);
pageContent.appendChild(widget);
button.style.display = 'block';
}
}
}
// Toggle widget visibility
const onWidgetToggle = () => {
widget.style.display = widget.style.display === 'none' ? 'block' : 'none';
}
button.onclick = onWidgetToggle;
// Load widget with onTrack callback
SIR("addWidget", "#widget", "match.scoreboard", {
matchId: 43406689,
onTrack: onTrack
});
</script>
</body>
</html>You can extend this pattern to handle multiple widgets:
const widgets = [
{ id: 'widget-1', matchId: 43406689 },
{ id: 'widget-2', matchId: 43406690 },
{ id: 'widget-3', matchId: 43406691 }
];
const createOnTrack = (widgetId, buttonId) => {
return (eventType, data) => {
if (eventType === 'data_change' && !data.error) {
const widget = document.getElementById(widgetId);
const button = document.getElementById(buttonId);
const loadingArea = document.getElementById('loading-area');
const pageContent = document.getElementById('your-content');
loadingArea.removeChild(widget);
pageContent.appendChild(widget);
button.style.display = 'block';
}
};
};
// Load each widget with its own callback
widgets.forEach((config, index) => {
SIR("addWidget", `#${config.id}`, "match.scoreboard", {
matchId: config.matchId,
onTrack: createOnTrack(config.id, `toggle-${index}`)
});
});Handle errors explicitly with custom messaging:
const onTrack = (eventType, data) => {
if (eventType === 'data_change') {
if (!data.error) {
// Success: move widget and show controls
loadingArea.removeChild(widget);
pageContent.appendChild(widget);
button.style.display = 'block';
} else {
// Error: log and show fallback
console.error('Widget failed to load:', data.error);
// Optional: Show fallback message
const fallback = document.createElement('div');
fallback.className = 'widget-error-message';
fallback.textContent = 'Content temporarily unavailable';
pageContent.appendChild(fallback);
}
}
};Symptom: The toggle button shows up, but clicking it doesn't display the widget.
Cause: The widget wasn't properly moved from the loading area.
Solution: Verify the DOM manipulation in your onTrack callback:
// Check in browser console
console.log('Widget parent:', widget.parentElement);
// Should show #your-content, not #loading-areaSymptom: Widget flashes on screen then vanishes.
Cause: Multiple data_change events firing without proper flag management.
Solution: Add a flag to track widget movement:
let widgetMoved = false;
const onTrack = (eventType, data) => {
if (eventType === 'data_change' && !data.error && !widgetMoved) {
loadingArea.removeChild(widget);
pageContent.appendChild(widget);
button.style.display = 'block';
widgetMoved = true;
}
};Symptom: Uncaught DOMException: Failed to execute 'removeChild'
Cause: Trying to remove a widget that isn't a child of the loading area.
Solution: Check parent before removing:
if (widget.parentElement === loadingArea) {
loadingArea.removeChild(widget);
pageContent.appendChild(widget);
}Load widgets in a hidden container to prevent displaying broken widgets or errors. Only move them to visible areas after confirming successful initialization.
Use !data.error in the onTrack callback to programmatically detect successful widget loading before showing UI controls.
Moving widgets between containers with removeChild and appendChild gives you full control over when and where widgets appear.
onTrack usage for loading states