---
title: "Conditional Widget Display With Error Handling"
canonical_url: "https://apidocs.sportradar.com/resources/widgets/docs/tutorials/examples/example-widget-ontrack-error"
markdown_url: "https://apidocs.sportradar.com/resources/widgets/docs/tutorials/examples/example-widget-ontrack-error.md"
last_updated: "2026-03-23T13:02:40Z"
---

# Conditional Widget Display With Error Handling

## Intended Audience

This tutorial is for:

- **Frontend developers** building conditional UI elements that depend on widget state
- **UX engineers** creating graceful error handling experiences
- Developers familiar with [basic widget integration](https://apidocs.sportradar.com/resources/widgets/docs/tutorials.md) and [onTrack callbacks](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/examples/example-widget-loading.md)
- Those building tab interfaces, conditional buttons, or dynamic widget displays

## Goals

By completing this tutorial, you will:

- Understand when and why to display widgets conditionally
- Implement hidden widget loading with error detection
- Use `onTrack` callback to detect successful widget initialization
- Display UI controls only when widgets load successfully
- Move widgets between DOM containers dynamically
- Handle widget errors without showing broken UI elements

## Prerequisites

Before starting this tutorial, ensure you have:

- **Valid Sportradar Widget license** - Contact [Sales](mailto:sales@betradar.com) if needed
- **Basic HTML/CSS/JavaScript knowledge** - DOM manipulation, event handling, element styling
- **Completed [Widget Loading Tutorial](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/examples/example-widget-loading.md)** - Understanding of `onTrack` callback
- **Web development environment** - Text editor and modern browser with DevTools

> **Info**
>
> This tutorial builds on the concepts from the [Adding Loading Indicators](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/examples/example-widget-loading.md) tutorial. If you're new to `onTrack` callbacks, review that tutorial first.

***

## Overview

### The Challenge

In many UX scenarios, you want to show UI controls (buttons, tabs, toggles) only when the associated widget loads successfully:

- **Tab interfaces** - Display tabs only for widgets that loaded without errors
- **Toggle buttons** - Show "Show Widget" buttons only if the widget is available
- **Conditional features** - Enable features that depend on widget data

> **Warning**
>
> Using the `silent` error mode hides errors but doesn't help you conditionally display UI elements. You need programmatic error detection.

### The Solution

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.

***

## Tutorial Steps

### Step 1: Set up HTML Structure With Hidden Containers

Create two separate areas: a hidden loading area for widgets, and a visible content area for UI controls.

```html {9-16}
<!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>
```

#### Understanding the Structure

### Visible Content Area

`#your-content` - Contains UI controls that should only appear when the widget loads successfully. Initially, the button is hidden.

### Hidden Loading Area

`#loading-area` - Hidden container where widgets load. This prevents users from seeing widgets during initialization or if errors occur.

### Widget Container

`#widget` - The actual widget element, initially hidden with `display: none` within the hidden loading area.

> **Tip**
>
> This double-hiding approach (loading area hidden AND widget hidden) gives you fine-grained control over when and where the widget appears.

***

### Step 2: Get DOM Element References

Obtain references to all HTML elements you'll need to manipulate.

```js
// 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');
```

***

### Step 3: Initialize the Widgetloader

Load the widget framework with your configuration.

```js
(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"
});
```

***

### Step 4: Implement the onTrack Callback With Error Detection

Create a callback that listens for successful widget loading and moves the widget to the visible area.

```js {3-8}
// 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
    }
}
```

#### What Happens Here

**Understanding the Logic Flow**

1. **Wait for `data_change` event** - Fired when widget receives data (successful or error)
2. **Check for errors** - `!data.error` means no errors occurred
3. **Move widget to visible area** - Remove from loading area, append to content area
4. **Show the button** - Display the toggle control
5. **On error** - Widget and button both remain hidden

> **Info**
>
> The widget continues to exist in the hidden loading area if an error occurs. Users never see broken widgets or empty spaces.

***

### Step 5: Implement Toggle Functionality

Create a function to toggle widget visibility when the button is clicked.

```js {2-4}
// Toggle widget visibility on button click
const onWidgetToggle = () => {
    widget.style.display = widget.style.display === 'none' ? 'block' : 'none';
}
button.onclick = onWidgetToggle;
```

> **Note**
>
> This toggle function only becomes accessible because the button is only displayed when the widget loaded successfully.

***

### Step 6: Load the Widget With Configuration

Initialize the widget with the `onTrack` callback.

```js {3}
SIR("addWidget", "#widget", "match.scoreboard", {
    matchId: 43406689,
    onTrack: onTrack
});
```

***

## Complete Implementation

**View Complete Working Example**

```html
<!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>
```

***

## Advanced Use Cases

### Loading Multiple Widgets With Conditional Display

You can extend this pattern to handle multiple widgets:

**Multiple Widgets**

```js
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}`)
    });
});
```

**Tab Interface**

```js
// Show tab only if widget loads successfully
const onTrack = (eventType, data) => {
    if (eventType === 'data_change' && !data.error) {
        // Move widget to tab content area
        const tabContent = document.getElementById('tab-content-scoreboard');
        const widget = document.getElementById('widget');
        tabContent.appendChild(widget);
        
        // Show the tab
        const tab = document.getElementById('scoreboard-tab');
        tab.style.display = 'block';
    }
};
```

### Error Logging and Fallback Content

Handle errors explicitly with custom messaging:

```js {4-10}
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);
        }
    }
};
```

***

## Troubleshooting

### Issue: Button Appears but Widget Doesn't

**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:

```js
// Check in browser console
console.log('Widget parent:', widget.parentElement);
// Should show #your-content, not #loading-area
```

### Issue: Widget Shows Briefly Then Disappears

**Symptom:** Widget flashes on screen then vanishes.

**Cause:** Multiple `data_change` events firing without proper flag management.

**Solution:** Add a flag to track widget movement:

```js {1,4,11}
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;
    }
};
```

### Issue: Console Errors About removeChild

**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:

```js {2}
if (widget.parentElement === loadingArea) {
    loadingArea.removeChild(widget);
    pageContent.appendChild(widget);
}
```

***

## Key Takeaways

### Hidden Loading Area

Load widgets in a hidden container to prevent displaying broken widgets or errors. Only move them to visible areas after confirming successful initialization.

### Error Detection

Use `!data.error` in the `onTrack` callback to programmatically detect successful widget loading before showing UI controls.

### DOM Manipulation

Moving widgets between containers with `removeChild` and `appendChild` gives you full control over when and where widgets appear.

***

## Further Reading

### Core Documentation

- [Widget Events (onTrack)](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/widgets-events.md) - Complete event reference and all event types
- [Error Handling Guide](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/troubleshooting/widget-errors.md) - Comprehensive error handling strategies
- [Widget Configuration](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/widgets-configuration.md) - All widget configuration options

### Related Tutorials

- [Adding Loading Indicators](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/examples/example-widget-loading.md) - Basic `onTrack` usage for loading states
- [Widget Visibility in Carousels](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/examples/example-widget-carousels-scrollers-sliders.md) - Managing visibility in dynamic layouts

### Advanced Topics

- [Silent Error Mode](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/troubleshooting/licensing-errors.md) - When to use silent mode vs programmatic detection
- [Dynamic Widget Loading](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/advanced/dynamic-loading.md) - Loading widgets on demand
- [Performance Best Practices](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/advanced/performance.md) - Optimizing widget loading
