Skip to main content
Version: 11.13.0

Adding buttons to the UI

This example adds an Info menu, a Skip intro button and seek back/forward buttons to the player.

Open Video UI​

Tip: open your browser's developer console and use the player and THEOplayer variables to interact with this demo.

The Info button opens a menu with live player values. Skip intro seeks to 10 seconds and disappears after that point. Two <theoplayer-seek-button> elements in the bottom-control-bar seek 10 seconds back and forward (hidden on mobile, where the centered seek buttons already do this).

Code
demo.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Adding buttons to the UI</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet" />
<style>
html,
body {
margin: 0;
padding: 0;
}

theoplayer-default-ui:not(:defined) {
display: inline-block;
box-sizing: border-box;
}

theoplayer-default-ui {
width: 100%;
aspect-ratio: 16 / 9;
font-family: 'Noto Sans', sans-serif;
background: #000;
}

#info-menu {
box-sizing: border-box;
max-width: min(22rem, calc(100vw - 2rem));
padding: 1rem;
color: #fff;
}

#info-menu [slot='heading'] {
font-weight: 700;
}

.info-values {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.35rem 1rem;
margin: 0.75rem 0 0;
}

.info-values dt {
opacity: 0.75;
}

.info-values dd {
margin: 0;
text-align: right;
}

.info-icon {
width: 1.25rem;
height: 1.25rem;
vertical-align: middle;
}
</style>
<script src="https://cdn.theoplayer.com/dash/theoplayer/THEOplayer.js"></script>
<script nomodule src="https://unpkg.com/@theoplayer/web-ui@2/polyfills"></script>
<script async src="https://unpkg.com/@theoplayer/web-ui@2"></script>
</head>
<body>
<!--
libraryLocation: For demonstration purposes, we use the THEOplayer CDN.
For production use, host the library yourself and update this URL and the script above.

licenseUrl: Point this to your license file, or replace it with a license property.
-->
<theoplayer-default-ui
id="player-ui"
configuration='{"libraryLocation":"https://cdn.theoplayer.com/dash/theoplayer/","licenseUrl":"../../../../theoplayer-license.txt"}'
source='{"sources":{"src":"https://cdn.theoplayer.com/video/big_buck_bunny/big_buck_bunny_metadata.m3u8","type":"application/x-mpegurl"},"poster":"https://cdn.theoplayer.com/video/big_buck_bunny/poster.jpg","metadata":{"title":"Big Buck Bunny"}}'
>
<theoplayer-button id="skip-intro" slot="top-control-bar">Skip intro</theoplayer-button>
<theoplayer-seek-button slot="bottom-control-bar" seek-offset="-10" mobile-hidden></theoplayer-seek-button>
<theoplayer-seek-button slot="bottom-control-bar" seek-offset="10" mobile-hidden></theoplayer-seek-button>
<theoplayer-menu-button slot="bottom-control-bar" menu="info-menu" aria-label="Info">
<svg class="info-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2" />
<path d="M12 11v6m0-10h.01" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
</svg>
</theoplayer-menu-button>
<theoplayer-menu id="info-menu" slot="menu" hidden>
<span slot="heading">About this video</span>
<p>Big Buck Bunny is a short animated film by the Blender Foundation.</p>
<dl class="info-values">
<dt>Quality</dt>
<dd id="current-quality">Loading</dd>
<dt>Duration</dt>
<dd id="duration">Loading</dd>
</dl>
</theoplayer-menu>
</theoplayer-default-ui>
<script src="../utils.js"></script>
<script>
const ui = document.querySelector('#player-ui');
const skipButton = document.querySelector('#skip-intro');
const qualityValue = document.querySelector('#current-quality');
const durationValue = document.querySelector('#duration');

function formatDuration(duration) {
if (!Number.isFinite(duration)) return 'Live';
const minutes = Math.floor(duration / 60);
const seconds = Math.floor(duration % 60);
return `${minutes}:${String(seconds).padStart(2, '0')}`;
}

onPlayerReady((player) => {
function updateInfo() {
const track = Array.from(player.videoTracks).find((videoTrack) => videoTrack.enabled);
const quality = track && track.activeQuality;
qualityValue.textContent = quality ? `${quality.height}p` : '—';
durationValue.textContent = formatDuration(player.duration);
skipButton.hidden = player.currentTime > 10;
}

skipButton.addEventListener('click', () => {
player.currentTime = 10;
});
player.addEventListener(['durationchange', 'timeupdate'], updateInfo);
player.videoTracks.addEventListener('addtrack', (event) => {
event.track.addEventListener('activequalitychanged', updateInfo);
updateInfo();
});
updateInfo();
});
</script>
</body>
</html>

Default UI​

The Default UI is created with new THEOplayer.Player(). Its Info button toggles an overlay inside the player, and two video.js Button components inserted after the play toggle seek 10 seconds back and forward, clamped to the content duration.

Code
default-ui.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Adding buttons to the UI (Default UI)</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://cdn.theoplayer.com/dash/theoplayer/ui.css" />
<script src="https://cdn.theoplayer.com/dash/theoplayer/THEOplayer.js"></script>
<style>
html,
body {
margin: 0;
padding: 0;
}

#player {
position: relative;
width: 100%;
}

.custom-info-button {
width: 3rem;
}

.custom-info-button svg {
width: 1.25rem;
height: 1.25rem;
vertical-align: middle;
}

.custom-info-overlay {
position: absolute;
z-index: 10;
right: 1rem;
bottom: 4.5rem;
box-sizing: border-box;
width: min(22rem, calc(100% - 2rem));
padding: 1rem;
border-radius: 0.4rem;
background: rgb(0 0 0 / 90%);
color: #fff;
font:
14px/1.5 Arial,
sans-serif;
}

.custom-info-overlay[hidden] {
display: none;
}

.custom-seek-button {
width: 3rem;
cursor: pointer;
}

.custom-seek-button svg {
width: 1.25rem;
height: 1.25rem;
vertical-align: middle;
}

.custom-info-overlay h2 {
margin: 0 2rem 0.5rem 0;
font-size: 1rem;
}

.custom-info-overlay p {
margin: 0;
}

.custom-info-close {
position: absolute;
top: 0.5rem;
right: 0.5rem;
border: 0;
background: transparent;
color: inherit;
font: inherit;
font-size: 1.25rem;
cursor: pointer;
}
</style>
</head>
<body>
<div id="player" class="video-js theoplayer-skin vjs-16-9"></div>
<script>
var playerElement = document.querySelector('#player');
var player = new THEOplayer.Player(playerElement, {
libraryLocation: 'https://cdn.theoplayer.com/dash/theoplayer/',
licenseUrl: '../../../../theoplayer-license.txt',
});
player.source = {
sources: [
{
src: 'https://cdn.theoplayer.com/video/big_buck_bunny/big_buck_bunny_metadata.m3u8',
type: 'application/x-mpegurl',
},
],
poster: 'https://cdn.theoplayer.com/video/big_buck_bunny/poster.jpg',
metadata: { title: 'Big Buck Bunny' },
};

var videojs = THEOplayer.videojs;
var overlay = document.createElement('div');
overlay.className = 'custom-info-overlay';
overlay.hidden = true;
overlay.innerHTML =
'<button class="custom-info-close" type="button" aria-label="Close">×</button>' +
'<h2>About this video</h2>' +
'<p>Big Buck Bunny is a short animated film by the Blender Foundation.</p>';

var InfoButton = videojs.extend(videojs.getComponent('Button'), {
constructor: function (player, options) {
videojs.getComponent('Button').call(this, player, options);
this.controlText('Info');
},
buildCSSClass: function () {
return 'custom-info-button vjs-control vjs-button';
},
handleClick: function () {
overlay.hidden = !overlay.hidden;
},
});
videojs.registerComponent('InfoButton', InfoButton);

// Seek back/forward 10 seconds, clamped to the content duration.
function registerSeekButton(name, offset, label, iconPath) {
var SeekButton = videojs.extend(videojs.getComponent('Button'), {
constructor: function (player, options) {
videojs.getComponent('Button').call(this, player, options);
this.controlText(label);
},
buildCSSClass: function () {
return 'custom-seek-button vjs-control vjs-button';
},
handleClick: function () {
player.currentTime = Math.max(0, Math.min(player.duration || Infinity, player.currentTime + offset));
},
});
videojs.registerComponent(name, SeekButton);
var button = player.ui.getChild('controlBar').addChild(name, {});
var icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
icon.setAttribute('viewBox', '0 0 24 24');
icon.setAttribute('fill', 'none');
icon.setAttribute('aria-hidden', 'true');
icon.innerHTML = iconPath;
button.el().insertBefore(icon, button.el().firstChild);
button.el().setAttribute('aria-label', label);
return button;
}

var seekBack = registerSeekButton(
'SeekBackButton',
-10,
'Seek back 10 seconds',
'<path d="M12 5V2L7 7l5 5V8c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6H4c0 4.4 3.6 8 8 8s8-3.6 8-8-3.6-8-8-8z" fill="currentColor"></path>'
);
var seekForward = registerSeekButton(
'SeekForwardButton',
10,
'Seek forward 10 seconds',
'<path d="M12 5V2l5 5-5 5V8c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6h2c0 4.4-3.6 8-8 8s-8-3.6-8-8 3.6-8 8-8z" fill="currentColor"></path>'
);
// Place the seek buttons right after the play toggle in the control bar.
var controlBar = player.ui.getChild('controlBar');
controlBar.el().insertBefore(seekForward.el(), controlBar.el().children[1]);
controlBar.el().insertBefore(seekBack.el(), controlBar.el().children[1]);

var infoButton = player.ui.getChild('controlBar').addChild('InfoButton', {});
var icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
icon.setAttribute('viewBox', '0 0 24 24');
icon.setAttribute('fill', 'none');
icon.setAttribute('aria-hidden', 'true');
icon.innerHTML =
'<circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2"></circle><path d="M12 11v6m0-10h.01" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path>';
infoButton.el().insertBefore(icon, infoButton.el().firstChild);
infoButton.el().setAttribute('aria-label', 'Info');
playerElement.appendChild(overlay);
overlay.querySelector('.custom-info-close').addEventListener('click', function () {
overlay.hidden = true;
});
</script>
</body>
</html>