# 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?](#what-is-a-component)
* [Creating a Component](#creating-a-component)
* [Component Children](#component-children)
  * [Basic Example](#basic-example)
  * [Using Options](#using-options)
* [Event Listening](#event-listening)
  * [Using on](#using-on)
  * [Using off](#using-off)
  * [Using one](#using-one)
  * [Using trigger](#using-trigger)
* [Default Component Tree](#default-component-tree)
* [Specific Component Details](#specific-component-details)
  * [Play Toggle](#play-toggle)
  * [Volume Panel](#volume-panel)
  * [Text Track Settings](#text-track-settings)
  * [Resize Manager](#resize-manager)

## 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][api].

## 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](https://jsbin.com/vobacas/edit?html,css,js,output) 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:

```js
// 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

```html
<div class="video-js">
  <div class="vjs-button">Button</div>
</div>
```

Adding the new button to the player

```js
// 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][api] 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:

```js
// 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:

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

```js
player.removeChild('BigPlayButton');
```

Results in a DOM that looks like this:

```html
<!-- Player Element -->
<div class="video-js">
</div>
```

### Using Options

Pass in options for child constructors and options for children of the child.

```js
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.

```js
// 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`

```js
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.

```js
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`

```js
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**.

```js
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`

```js
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.

```js
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`

```js
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:

```tree
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

```js
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`

```js
let player = videojs('myplayer', {
  controlBar: {
    volumePanel: {
      inline: false
    }
  }
});
```

### Text Track Settings

The text track settings component is only available when using emulated text tracks.

[api]: https://docs.videojs.com/Component.html

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

```js
var player = videojs('myplayer', {
  resizeManager: {
    ResizeObserver: ResizeObserverPoylfill
  }
});
```

To force using the iframe fallback, pass in `null` as the `ResizeObserver`:

```js
var player = videojs('myplayer', {
  resizeManager: {
    ResizeObserver: null
  }
});
```

The ResizeManager can also just be disabled like so:

```js
var player = videojs('myplayer', {
  resizeManager: false
});
```
