or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdcore-media-element.mdfeature-system.mdindex.mdmedia-player.mdrenderer-system.mdutility-functions.md

feature-system.mddocs/

0

# Feature System

1

2

MediaElement.js uses a modular feature system that allows customization of player controls and functionality. Features are individual components that can be enabled, disabled, or customized to create tailored player experiences.

3

4

## Capabilities

5

6

### Feature Interface

7

8

All features follow a standardized interface for consistent integration with the player.

9

10

```javascript { .api }

11

interface Feature {

12

/**

13

* Build and initialize the feature

14

* @param player - MediaElementPlayer instance

15

* @param controls - Controls container element

16

* @param layers - Layers container for overlays

17

* @param media - MediaElement instance

18

*/

19

build(player: MediaElementPlayer, controls: HTMLElement, layers: HTMLElement, media: MediaElement): void;

20

21

/**

22

* Clean up and destroy the feature (optional)

23

* @param player - MediaElementPlayer instance

24

* @param layers - Layers container

25

* @param controls - Controls container

26

* @param media - MediaElement instance

27

*/

28

clean?(player: MediaElementPlayer, layers: HTMLElement, controls: HTMLElement, media: MediaElement): void;

29

}

30

31

// Features are added to MediaElementPlayer prototype

32

interface MediaElementPlayer {

33

// Feature methods follow naming pattern: build{FeatureName} and clean{FeatureName}

34

buildplaypause(player: MediaElementPlayer, controls: HTMLElement, layers: HTMLElement, media: MediaElement): void;

35

cleanplaypause?(player: MediaElementPlayer, layers: HTMLElement, controls: HTMLElement, media: MediaElement): void;

36

}

37

```

38

39

### Built-in Features

40

41

MediaElement.js includes several built-in features for common player functionality.

42

43

```javascript { .api }

44

/** Available built-in features */

45

const builtInFeatures = [

46

'playpause', // Play/pause toggle button

47

'current', // Current time display

48

'progress', // Progress bar with scrubbing

49

'duration', // Total duration display

50

'tracks', // Caption/subtitle controls

51

'volume', // Volume control with slider

52

'fullscreen' // Fullscreen toggle button

53

];

54

55

// Default feature configuration

56

const defaultFeatures = ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen'];

57

```

58

59

### Play/Pause Feature

60

61

Toggle button for controlling media playback.

62

63

```javascript { .api }

64

interface PlayPauseFeature extends Feature {

65

/**

66

* Build play/pause button control

67

*/

68

buildplaypause(player: MediaElementPlayer, controls: HTMLElement, layers: HTMLElement, media: MediaElement): void;

69

}

70

71

// CSS classes generated

72

interface PlayPauseClasses {

73

button: 'mejs__button mejs__playpause-button';

74

play: 'mejs__playpause-button > button.mejs__play';

75

pause: 'mejs__playpause-button > button.mejs__pause';

76

}

77

```

78

79

**Usage Examples:**

80

81

```javascript

82

// Enable play/pause feature

83

const player = new MediaElementPlayer('video', {

84

features: ['playpause']

85

});

86

87

// Access play/pause button after initialization

88

const playButton = player.controls.querySelector('.mejs__playpause-button button');

89

console.log('Play button:', playButton);

90

91

// Custom play/pause styling

92

player.container.addEventListener('controlsready', () => {

93

const playPauseBtn = player.controls.querySelector('.mejs__playpause-button');

94

playPauseBtn.style.order = '1'; // Position in flex layout

95

});

96

```

97

98

### Progress Feature

99

100

Interactive progress bar with scrubbing capabilities.

101

102

```javascript { .api }

103

interface ProgressFeature extends Feature {

104

/**

105

* Build progress bar control

106

*/

107

buildprogress(player: MediaElementPlayer, controls: HTMLElement, layers: HTMLElement, media: MediaElement): void;

108

}

109

110

// CSS classes generated

111

interface ProgressClasses {

112

rail: 'mejs__time-rail';

113

total: 'mejs__time-total';

114

loaded: 'mejs__time-loaded';

115

current: 'mejs__time-current';

116

handle: 'mejs__time-handle';

117

float: 'mejs__time-float';

118

floatCurrent: 'mejs__time-float-current';

119

floatCorner: 'mejs__time-float-corner';

120

marker: 'mejs__time-marker';

121

}

122

```

123

124

**Usage Examples:**

125

126

```javascript

127

// Enable progress bar

128

const player = new MediaElementPlayer('video', {

129

features: ['progress']

130

});

131

132

// Access progress elements

133

player.container.addEventListener('controlsready', () => {

134

const progressRail = player.controls.querySelector('.mejs__time-rail');

135

const currentProgress = player.controls.querySelector('.mejs__time-current');

136

137

console.log('Progress rail:', progressRail);

138

console.log('Current progress:', currentProgress);

139

});

140

141

// Listen for progress events

142

player.media.addEventListener('progress', () => {

143

const buffered = player.media.buffered;

144

if (buffered.length > 0) {

145

const loadedPercent = (buffered.end(0) / player.media.duration) * 100;

146

console.log(`Loaded: ${loadedPercent}%`);

147

}

148

});

149

```

150

151

### Time Display Features

152

153

Current time and duration display components.

154

155

```javascript { .api }

156

interface CurrentTimeFeature extends Feature {

157

/**

158

* Build current time display

159

*/

160

buildcurrent(player: MediaElementPlayer, controls: HTMLElement, layers: HTMLElement, media: MediaElement): void;

161

}

162

163

interface DurationFeature extends Feature {

164

/**

165

* Build duration display

166

*/

167

buildduration(player: MediaElementPlayer, controls: HTMLElement, layers: HTMLElement, media: MediaElement): void;

168

}

169

170

// CSS classes generated

171

interface TimeClasses {

172

time: 'mejs__time';

173

timeCurrent: 'mejs__time mejs__currenttime-container';

174

timeDuration: 'mejs__time mejs__duration-container';

175

}

176

```

177

178

**Usage Examples:**

179

180

```javascript

181

// Enable time displays

182

const player = new MediaElementPlayer('video', {

183

features: ['current', 'duration'],

184

// Time format options

185

alwaysShowHours: false,

186

showTimecodeFrameCount: false,

187

timeFormat: 'mm:ss'

188

});

189

190

// Access time display elements

191

player.container.addEventListener('controlsready', () => {

192

const currentTime = player.controls.querySelector('.mejs__currenttime-container');

193

const duration = player.controls.querySelector('.mejs__duration-container');

194

195

console.log('Current time element:', currentTime);

196

console.log('Duration element:', duration);

197

});

198

199

// Custom time format

200

const customTimePlayer = new MediaElementPlayer('video', {

201

features: ['current', 'duration'],

202

alwaysShowHours: true,

203

timeFormat: 'hh:mm:ss'

204

});

205

```

206

207

### Volume Feature

208

209

Volume control with mute toggle and volume slider.

210

211

```javascript { .api }

212

interface VolumeFeature extends Feature {

213

/**

214

* Build volume control

215

*/

216

buildvolume(player: MediaElementPlayer, controls: HTMLElement, layers: HTMLElement, media: MediaElement): void;

217

}

218

219

// CSS classes generated

220

interface VolumeClasses {

221

button: 'mejs__button mejs__volume-button';

222

slider: 'mejs__volume-slider';

223

rail: 'mejs__volume-rail';

224

handle: 'mejs__volume-handle';

225

current: 'mejs__volume-current';

226

icon: 'mejs__volume-icon';

227

}

228

```

229

230

**Usage Examples:**

231

232

```javascript

233

// Enable volume control

234

const player = new MediaElementPlayer('video', {

235

features: ['volume']

236

});

237

238

// Access volume elements

239

player.container.addEventListener('controlsready', () => {

240

const volumeButton = player.controls.querySelector('.mejs__volume-button');

241

const volumeSlider = player.controls.querySelector('.mejs__volume-slider');

242

243

console.log('Volume button:', volumeButton);

244

console.log('Volume slider:', volumeSlider);

245

});

246

247

// Listen for volume changes

248

player.media.addEventListener('volumechange', () => {

249

console.log(`Volume: ${player.media.volume}, Muted: ${player.media.muted}`);

250

});

251

252

// Set initial volume

253

player.container.addEventListener('controlsready', () => {

254

player.setVolume(0.8); // 80% volume

255

});

256

```

257

258

### Fullscreen Feature

259

260

Fullscreen toggle functionality with browser API integration.

261

262

```javascript { .api }

263

interface FullscreenFeature extends Feature {

264

/**

265

* Build fullscreen control

266

*/

267

buildfullscreen(player: MediaElementPlayer, controls: HTMLElement, layers: HTMLElement, media: MediaElement): void;

268

}

269

270

// CSS classes generated

271

interface FullscreenClasses {

272

button: 'mejs__button mejs__fullscreen-button';

273

unfullscreen: 'mejs__unfullscreen';

274

isFullscreen: 'mejs__container-fullscreen';

275

}

276

```

277

278

**Usage Examples:**

279

280

```javascript

281

// Enable fullscreen control

282

const player = new MediaElementPlayer('video', {

283

features: ['fullscreen']

284

});

285

286

// Listen for fullscreen changes

287

player.container.addEventListener('controlsready', () => {

288

player.media.addEventListener('enterfullscreen', () => {

289

console.log('Entered fullscreen');

290

});

291

292

player.media.addEventListener('exitfullscreen', () => {

293

console.log('Exited fullscreen');

294

});

295

});

296

297

// Programmatic fullscreen control

298

player.enterFullScreen();

299

player.exitFullScreen();

300

301

// Check fullscreen state

302

if (player.isFullScreen) {

303

console.log('Currently in fullscreen');

304

}

305

```

306

307

### Tracks Feature

308

309

Caption and subtitle track management.

310

311

```javascript { .api }

312

interface TracksFeature extends Feature {

313

/**

314

* Build tracks/captions control

315

*/

316

buildtracks(player: MediaElementPlayer, controls: HTMLElement, layers: HTMLElement, media: MediaElement): void;

317

}

318

319

// CSS classes generated

320

interface TracksClasses {

321

button: 'mejs__button mejs__captions-button';

322

selector: 'mejs__captions-selector';

323

translations: 'mejs__captions-translations';

324

layer: 'mejs__captions-layer';

325

text: 'mejs__captions-text';

326

}

327

```

328

329

**Usage Examples:**

330

331

```javascript

332

// Enable tracks/captions

333

const player = new MediaElementPlayer('video', {

334

features: ['tracks']

335

});

336

337

// Add tracks programmatically

338

player.container.addEventListener('controlsready', () => {

339

// Tracks are typically added via HTML track elements

340

const track = document.createElement('track');

341

track.kind = 'subtitles';

342

track.src = 'captions.vtt';

343

track.srclang = 'en';

344

track.label = 'English';

345

track.default = true;

346

347

player.media.appendChild(track);

348

});

349

350

// Listen for track changes

351

player.media.addEventListener('loadedmetadata', () => {

352

const tracks = player.media.textTracks;

353

console.log(`Found ${tracks.length} text tracks`);

354

355

for (let i = 0; i < tracks.length; i++) {

356

const track = tracks[i];

357

console.log(`Track ${i}: ${track.label} (${track.language})`);

358

}

359

});

360

```

361

362

### Custom Feature Development

363

364

Create custom features using the standardized interface.

365

366

```javascript { .api }

367

/**

368

* Custom feature implementation example

369

*/

370

Object.assign(MediaElementPlayer.prototype, {

371

/**

372

* Build custom speed control feature

373

*/

374

buildspeed(player, controls, layers, media) {

375

const speedButton = document.createElement('div');

376

speedButton.className = `${player.options.classPrefix}button ${player.options.classPrefix}speed-button`;

377

speedButton.innerHTML = `

378

<button type="button">1x</button>

379

<div class="${player.options.classPrefix}speed-selector">

380

<ul>

381

<li><button data-speed="0.5">0.5x</button></li>

382

<li><button data-speed="1" class="selected">1x</button></li>

383

<li><button data-speed="1.25">1.25x</button></li>

384

<li><button data-speed="1.5">1.5x</button></li>

385

<li><button data-speed="2">2x</button></li>

386

</ul>

387

</div>

388

`;

389

390

// Add to controls

391

controls.appendChild(speedButton);

392

393

// Event handlers

394

const mainButton = speedButton.querySelector('button');

395

const selector = speedButton.querySelector(`.${player.options.classPrefix}speed-selector`);

396

const speedButtons = selector.querySelectorAll('button');

397

398

mainButton.addEventListener('click', () => {

399

selector.style.display = selector.style.display === 'block' ? 'none' : 'block';

400

});

401

402

speedButtons.forEach(btn => {

403

btn.addEventListener('click', (e) => {

404

const speed = parseFloat(e.target.dataset.speed);

405

media.playbackRate = speed;

406

mainButton.textContent = `${speed}x`;

407

408

// Update selected state

409

speedButtons.forEach(b => b.classList.remove('selected'));

410

e.target.classList.add('selected');

411

412

selector.style.display = 'none';

413

});

414

});

415

416

// Store reference for cleanup

417

player.speedButton = speedButton;

418

},

419

420

/**

421

* Clean up custom speed feature

422

*/

423

cleanspeed(player, layers, controls, media) {

424

if (player.speedButton) {

425

player.speedButton.remove();

426

delete player.speedButton;

427

}

428

}

429

});

430

```

431

432

**Usage Examples:**

433

434

```javascript

435

// Use custom speed feature

436

const player = new MediaElementPlayer('video', {

437

features: ['playpause', 'progress', 'speed', 'volume'] // Include custom 'speed' feature

438

});

439

440

// Custom feature with configuration

441

Object.assign(MediaElementPlayer.prototype, {

442

buildquality(player, controls, layers, media) {

443

// Check if HLS renderer is active

444

if (player.media.rendererName !== 'hls') {

445

return; // Only show for HLS streams

446

}

447

448

const qualityButton = document.createElement('div');

449

qualityButton.className = `${player.options.classPrefix}button ${player.options.classPrefix}quality-button`;

450

451

// Implementation details...

452

controls.appendChild(qualityButton);

453

454

player.qualityButton = qualityButton;

455

}

456

});

457

```

458

459

### Feature Configuration

460

461

Control feature behavior through player options.

462

463

```javascript { .api }

464

interface FeatureConfiguration {

465

/** List of features to enable */

466

features?: string[];

467

468

/** Use default feature set */

469

useDefaultControls?: boolean;

470

471

/** CSS class prefix for feature elements */

472

classPrefix?: string;

473

474

/** Feature-specific options */

475

[featureName: string]: any;

476

}

477

```

478

479

**Usage Examples:**

480

481

```javascript

482

// Minimal feature set

483

const minimalPlayer = new MediaElementPlayer('video', {

484

features: ['playpause', 'progress']

485

});

486

487

// Custom feature order (affects display order)

488

const customOrderPlayer = new MediaElementPlayer('video', {

489

features: ['volume', 'playpause', 'progress', 'current', 'duration', 'fullscreen']

490

});

491

492

// All features with custom prefix

493

const styledPlayer = new MediaElementPlayer('video', {

494

features: ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen'],

495

classPrefix: 'myplayer-',

496

useDefaultControls: false

497

});

498

499

// Feature-specific configuration

500

const configuredPlayer = new MediaElementPlayer('video', {

501

features: ['playpause', 'progress', 'volume'],

502

// Volume-specific options could be added here

503

volumeStep: 0.1, // Custom option for volume feature

504

progressHover: 'time' // Custom option for progress feature

505

});

506

```

507

508

### Feature Events

509

510

Features can dispatch and listen for custom events.

511

512

```javascript { .api }

513

// Common feature events

514

const featureEvents = [

515

'controlsready', // All features built

516

'controlsshown', // Controls became visible

517

'controlshidden', // Controls hidden

518

'featurebuilt', // Individual feature built

519

'featureclean' // Individual feature cleaned

520

];

521

```

522

523

**Usage Examples:**

524

525

```javascript

526

const player = new MediaElementPlayer('video', {

527

features: ['playpause', 'progress', 'volume'],

528

success: (mediaElement, originalNode, instance) => {

529

530

// Listen for feature events

531

instance.addEventListener('controlsready', () => {

532

console.log('All controls ready');

533

534

// Customize controls after they're built

535

const progressRail = instance.controls.querySelector('.mejs__time-rail');

536

progressRail.style.height = '8px';

537

});

538

539

instance.addEventListener('controlsshown', () => {

540

console.log('Controls shown');

541

});

542

543

instance.addEventListener('controlshidden', () => {

544

console.log('Controls hidden');

545

});

546

547

// Feature-specific events (if implemented by features)

548

mediaElement.addEventListener('volumechanged', (e) => {

549

console.log('Volume changed via feature:', e.detail.volume);

550

});

551

}

552

});

553

```

554

555

### Feature Lifecycle

556

557

Understanding the feature lifecycle helps in custom feature development.

558

559

```javascript { .api }

560

// Feature lifecycle phases

561

const lifecycle = {

562

1: 'Player initialization',

563

2: 'MediaElement creation',

564

3: 'Feature building (build* methods called)',

565

4: 'Controls ready event',

566

5: 'Player active',

567

6: 'Feature cleanup (clean* methods called)',

568

7: 'Player destruction'

569

};

570

```

571

572

The feature system provides a flexible architecture for extending MediaElement.js with custom functionality while maintaining consistency with built-in features.