Components
The architecture of the Video.js player is centered around components. The Player
class and all classes representing player controls and other UI elements inherit from the Component
class. This architecture makes it easy to construct the user interface of the Video.js player in a tree-like structure that mirrors the DOM.
Table of Contents
- What is a Component?
- Creating a Component
- Component Children
- Event Listening
- Default Component Tree
- Specific Component Details
What is a Component?
A component is a JavaScript object that has the following features:
- An associated DOM element, in almost all cases.
- An association to a
Player
object. - The ability to manage any number of child components.
- The ability to listen for and trigger events.
- A lifecycle of initialization and disposal.
For more specifics on the programmatic interface of a component, see the component API docs.
Creating a Component
Video.js components can be inherited and registered with Video.js to add new features and UI to the player.
For a working example, we have a JSBin demonstrating the creation of a component for displaying a title across the top of the player.
In addition, there are a couple methods worth recognizing:
videojs.getComponent(String name)
: Retrieves component constructors from Video.js.videojs.registerComponent(String name, Function Comp)
: Registers component constructors with Video.js.videojs.extend(Function component, Object properties)
: Provides prototype inheritance. Can be used to extend a component's constructor, returning a new constructor with the given properties.
Creation:
// adding a button to the player
var player = videojs('some-video-id');
var Button = videojs.getComponent('Button');
var button = new Button(player, {
clickHandler: function(event) {
videojs.log('Clicked');
}
});
console.log(button.el());
The above code will output
<div class="video-js">
<div class="vjs-button">Button</div>
</div>
Adding the new button to the player
// adding a button to the player
var player = videojs('some-video-id');
var button = player.addChild('button');
console.log(button.el());
// will have the same html result as the previous example
Component Children
Again, refer to the component API docs for complete details on methods available for managing component structures.
Basic Example
When child component is added to a parent component, Video.js inserts the element of the child into the element of the parent. For example, adding a component like this:
// Add a "BigPlayButton" component to the player. Its element will be appended to the player's element.
player.addChild('BigPlayButton');
Results in a DOM that looks like this:
<!-- Player Element -->
<div class="video-js">
<!-- BigPlayButton Element -->
<div class="vjs-big-play-button"></div>
</div>
Conversely, removing child components will remove the child component's element from the DOM:
player.removeChild('BigPlayButton');
Results in a DOM that looks like this:
<!-- Player Element -->
<div class="video-js">
</div>
Using Options
Pass in options for child constructors and options for children of the child.
var player = videojs('some-vid-id');
var Component = videojs.getComponent('Component');
var myComponent = new Component(player);
var myButton = myComponent.addChild('MyButton', {
text: 'Press Me',
buttonChildExample: {
buttonChildOption: true
}
});
Children can also be added via options when a component is initialized.
Note: Include a 'name' key which will be used if two child components of the same type that need different options.
// MyComponent is from the above example
var myComp = new MyComponent(player, {
children: ['button', {
name: 'button',
someOtherOption: true
}, {
name: 'button',
someOtherOption: false
}]
});
Event Listening
Using on
var player = videojs('some-player-id');
var Component = videojs.getComponent('Component');
var myComponent = new Component(player);
var myFunc = function() {
var myComponent = this;
console.log('myFunc called');
};
myComponent.on('eventType', myFunc);
myComponent.trigger('eventType');
// logs 'myFunc called'
The context of myFunc
will be myComponent
unless it is bound. You can add
a listener to another element or component.
var otherComponent = new Component(player);
// myComponent/myFunc is from the above example
myComponent.on(otherComponent.el(), 'eventName', myFunc);
myComponent.on(otherComponent, 'eventName', myFunc);
otherComponent.trigger('eventName');
// logs 'myFunc called' twice
Using off
var player = videojs('some-player-id');
var Component = videojs.getComponent('Component');
var myComponent = new Component(player);
var myFunc = function() {
var myComponent = this;
console.log('myFunc called');
};
myComponent.on('eventType', myFunc);
myComponent.trigger('eventType');
// logs 'myFunc called'
myComponent.off('eventType', myFunc);
myComponent.trigger('eventType');
// does nothing
If myFunc gets excluded, all listeners for the event type will get removed. If
eventType gets excluded, all listeners will get removed from the component.
You can use off
to remove listeners that get added to other elements or
components using:
myComponent.on(otherComponent...
In this case both the event type and listener function are REQUIRED.
var otherComponent = new Component(player);
// myComponent/myFunc is from the above example
myComponent.on(otherComponent.el(), 'eventName', myFunc);
myComponent.on(otherComponent, 'eventName', myFunc);
otherComponent.trigger('eventName');
// logs 'myFunc called' twice
myComponent.off(ootherComponent.el(), 'eventName', myFunc);
myComponent.off(otherComponent, 'eventName', myFunc);
otherComponent.trigger('eventName');
// does nothing
Using one
var player = videojs('some-player-id');
var Component = videojs.getComponent('Component');
var myComponent = new Component(player);
var myFunc = function() {
var myComponent = this;
console.log('myFunc called');
};
myComponent.one('eventName', myFunc);
myComponent.trigger('eventName');
// logs 'myFunc called'
myComponent.trigger('eventName');
// does nothing
You can also add a listener to another element or component that will get triggered only once.
var otherComponent = new Component(player);
// myComponent/myFunc is from the above example
myComponent.one(otherComponent.el(), 'eventName', myFunc);
myComponent.one(otherComponent, 'eventName', myFunc);
otherComponent.trigger('eventName');
// logs 'myFunc called' twice
otherComponent.trigger('eventName');
// does nothing
Using trigger
var player = videojs('some-player-id');
var Component = videojs.getComponent('Component');
var myComponent = new Component(player);
var myFunc = function(data) {
var myComponent = this;
console.log('myFunc called');
console.log(data);
};
myComponent.one('eventName', myFunc);
myComponent.trigger('eventName');
// logs 'myFunc called' and 'undefined'
myComponent.trigger({'type':'eventName'});
// logs 'myFunc called' and 'undefined'
myComponent.trigger('eventName', {data: 'some data'});
// logs 'myFunc called' and "{data: 'some data'}"
myComponent.trigger({'type':'eventName'}, {data: 'some data'});
// logs 'myFunc called' and "{data: 'some data'}"
Default Component Tree
The default component structure of the Video.js player looks something like this:
Player
├── MediaLoader (has no DOM element)
├── PosterImage
├── TextTrackDisplay
├── LoadingSpinner
├── BigPlayButton
├── LiveTracker (has no DOM element)
├─┬ ControlBar
│ ├── PlayToggle
│ ├── VolumePanel
│ ├── CurrentTimeDisplay (hidden by default)
│ ├── TimeDivider (hidden by default)
│ ├── DurationDisplay (hidden by default)
│ ├─┬ ProgressControl (hidden during live playback, except when liveui: true)
│ │ └─┬ SeekBar
│ │ ├── LoadProgressBar
│ │ ├── MouseTimeDisplay
│ │ └── PlayProgressBar
│ ├── LiveDisplay (hidden during VOD playback)
│ ├── SeekToLive (hidden during VOD playback)
│ ├── RemainingTimeDisplay
│ ├── CustomControlSpacer (has no UI)
│ ├── PlaybackRateMenuButton (hidden, unless playback tech supports rate changes)
│ ├── ChaptersButton (hidden, unless there are relevant tracks)
│ ├── DescriptionsButton (hidden, unless there are relevant tracks)
│ ├── SubtitlesButton (hidden, unless there are relevant tracks)
│ ├── CaptionsButton (hidden, unless there are relevant tracks)
│ ├── SubsCapsButton (hidden, unless there are relevant tracks)
│ ├── AudioTrackButton (hidden, unless there are relevant tracks)
│ ├── PictureInPictureToggle
│ └── FullscreenToggle
├── ErrorDisplay (hidden, until there is an error)
├── TextTrackSettings
└── ResizeManager (hidden)
Specific Component Details
Play Toggle
The PlayToggle
has one option replay
which can show or hide replay icon. This can be set by passing {replay: false}
as the default behavior replay icon is shown after video end playback.
Example of how to hide a replay icon
let player = videojs('myplayer', {
controlBar: {
playToggle: {
replay: false
}
}
});
Volume Panel
The VolumePanel
includes the MuteToggle
and the VolumeControl
Components, which will be hidden if volume changes are not supported. There is one important option for the VolumePanel
which can make your VolumeControl
appear vertically over the MuteToggle
. This can be set by passing VolumePanel
{inline: false}
as the default behavior is a horizontal VolumeControl
with {inline: true}
.
Example of a vertical VolumeControl
let player = videojs('myplayer', {
controlBar: {
volumePanel: {
inline: false
}
}
});
Text Track Settings
The text track settings component is only available when using emulated text tracks.
Resize Manager
This new component is in charge of triggering a playerresize
event when the player size changed.
It uses the ResizeObserver if available or a polyfill was provided. It has no element when using the ResizeObserver.
If a ResizeObserver is not available, it will fallback to an iframe element and listen to its resize event via a debounced handler.
A ResizeObserver polyfill can be passed in like so:
var player = videojs('myplayer', {
resizeManager: {
ResizeObserver: ResizeObserverPoylfill
}
});
To force using the iframe fallback, pass in null
as the ResizeObserver
:
var player = videojs('myplayer', {
resizeManager: {
ResizeObserver: null
}
});
The ResizeManager can also just be disabled like so:
var player = videojs('myplayer', {
resizeManager: false
});