The HTML5 specification introduced the <video>
element, which can be used to embed video content into web pages, and it is well supported across modern browsers. In this post, we'll learn how to embed an HTML5 video player on an HTML document and define custom controls for it that is independent of the browser defaults.
Here is a link to the demo CodeSandbox.
Setting Up the Project
Run the following command to create three files: index.html
, style.css
, script.js
in a folder called custom-video-player
.
1mkdir custom-video-player2cd custom-video-player3touch index.html style.css script.js
To embed a video player into our application, let’s add the following to our index.html
file:
1<!DOCTYPE html>2<html lang="en">3 <head>4 <meta charset="UTF-8" />5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />6 <title>Custom HTML5 Video Player</title>7 <link rel="stylesheet" href="style.css" />8 </head>9 <body>10 <h1>Video Player</h1>11 <div class="video-player">12 <video class="video" controls>13 <source14 src="https://res.cloudinary.com/ifeomaimoh/video/upload/v1650567487/speech_ofvfzq.mp4"15 type="video/mp4"16 />17 <source18 src="https://res.cloudinary.com/ifeomaimoh/video/upload/v1650567487/speech_ofvfzq.mp4"19 type="video/webm"20 />21 <p>Your browser doesn't support HTML5 video.</p>22 </video>23 </div>24 <script src="script.js"></script>25 </body>26</html>
In the code above, we rendered the video>
element. You can either add a video from your local computer to the src
attribute or go with the default Cloudinary link used in the code. The HTML5 specification currently supports three video formats, but we used multiple <source>
tags to make two formats of the same video available in MP4 and WebM. In addition, we specified pre-defined content to be displayed for user agents that do not support the video
element.
The video
tag also accepts several optional native attributes, such as controls
, which display the standard player controls interface when specified or set to true. Click here to find out more about other attributes.
Before we continue, let's add some styles to give our app a decent look. Populate your style.css
file with styles from this CodeSandbox link. If you preview the page in your browser, you should see the embedded video player as seen below:
Customizing the Video Player
Before customizing the video player, we need to remove the controls
attribute, which displays video controls such as Play
, Pause
, etc., and then build our custom controls. Update the <video>
element in your index.html
file as shown below:
1<video class="video">2 <source3 src="https://res.cloudinary.com/ifeomaimoh/video/upload/v1650352188/production.mp4"4 type="video/mp4"5 />6 <source7 src="https://res.cloudinary.com/ifeomaimoh/video/upload/v1650352188/production.mp4"8 type="video/webm"9 />10 <p>Your browser doesn't support HTML5 video.</p>11</video>
If you check your browser, you should see a non-interactive player.
Toggle the Pause/Play State
To add the pause and play functionality to our player, let’s update our index.html
file with the following:
1<div class="video-player">2 <video class="video">//...</video>3 <div class="controls">4 <button class="controls__button toggleButton" title="Toggle Play">►</button>5 </div>6</div>
Add the following to your script.js
file with the following to add some interactivity.
1const video = document.querySelector(".video");2const toggleButton = document.querySelector(".toggleButton");3function togglePlay() {4 if (video.paused || video.ended) {5 video.play();6 } else {7 video.pause();8 }9}10function updateToggleButton() {11 toggleButton.innerHTML = video.paused ? "►" : "❚❚";12}13toggleButton.addEventListener("click", togglePlay);14video.addEventListener("click", togglePlay);15video.addEventListener("play", updateToggleButton);16video.addEventListener("pause", updateToggleButton);
We started by selecting the video
element, which gives access to the video attributes and methods. We then selected the button
element and added an event listener to it that triggers the togglePlay
function when clicked. The togglePlay
function executes a condition that toggles playing or pausing the video based on its current state.
Next, we added a play
and pause
event listener to the video
element that calls the updateToggleButton
function, which updates based on the video’s state.
After that, you should be able to pause and play the video by clicking on the button or the video itself.
Implement Progress Bar
To add a progress bar that updates the video when played, update your index.html
file with the following:
1<h1>Video Player</h1>2<div class="video-player">3 //...4 <div class="controls">5 <div class="progress">6 <div class="progress__filled"></div>7 </div>8 <button class="controls__button toggleButton" title="Toggle Play">►</button>9 </div>10</div>
To make the progress bar both interactive, Update your script.js
file to look like this:
1const video = document.querySelector(".video");2const toggleButton = document.querySelector(".toggleButton");3const progress = document.querySelector(".progress");4const progressBar = document.querySelector(".progress__filled");56function togglePlay() {7 if (video.paused || video.ended) {8 video.play();9 } else {10 video.pause();11 }12}1314function updateToggleButton() {15 toggleButton.innerHTML = video.paused ? "►" : "❚ ❚";16}1718function handleProgress() {19 const progressPercentage = (video.currentTime / video.duration) * 100;20 progressBar.style.flexBasis = `${progressPercentage}%`;21}2223function scrub(e) {24 const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration;25 video.currentTime = scrubTime;26}2728toggleButton.addEventListener("click", togglePlay);29video.addEventListener("click", togglePlay);30video.addEventListener("play", updateToggleButton);31video.addEventListener("pause", updateToggleButton);3233video.addEventListener("timeupdate", handleProgress);34progress.addEventListener("click", scrub);35let mousedown = false;36progress.addEventListener("mousedown", () => (mousedown = true));37progress.addEventListener("mousemove", (e) => mousedown && scrub(e));38progress.addEventListener("mouseup", () => (mousedown = false));
We selected the progress bar DOM element and added an event listener to the video
object that executes the handleProgress
function on timeupdate
. The handleProgress
function calculates the percentage of the video’s current time relative to its total duration and then updates the style as the video progresses.
We also added a scrub
function to scrub the video to a specific point in relation to the progress bar.
Save the changes and preview the app in your browser to see the updated state of the video player.
Add Volume and Playback Rate Slider
To add custom sliders that will be used to manage the volume and playback rate states, update your index.html
file with the following:
1<div class="video-player">2 <video class="video">//...</video>3</div>4<div class="controls">5 <div class="progress">6 <div class="progress__filled"></div>7 </div>8 <button class="controls__button toggleButton" title="Toggle Play">►</button>910 // Add these11 <input12 type="range"13 name="volume"14 class="controls__slider"15 min="0"16 max="1"17 step="0.05"18 value="1"19 />20 <input21 type="range"22 name="playbackRate"23 class="controls__slider"24 min="0.5"25 max="2"26 step="0.1"27 value="1"28 />29</div>
We added two input
tags with a type
of range
with a predefined min
and max
value for the volume and playback rate, respectively.
Now update your script.js
file with the following code to make the sliders functional:
1const video = document.querySelector(".video");2const toggleButton = document.querySelector(".toggleButton");3const progress = document.querySelector(".progress");4const progressBar = document.querySelector(".progress__filled");5// Add this6const sliders = document.querySelectorAll(".contols__slider");7function togglePlay() {8 //...9}10function updateToggleButton() {11 //...12}13function handleProgress() {14 //...15}16function scrub(e) {17 //...18}19function handleSliderUpdate() {20 video[this.name] = this.value;21}22//...23sliders.forEach((slider) => {24 slider.addEventListener("change", handleSliderUpdate);25});
We selected the slider
elements and attached an event listener to each of them that triggers the handleSliderUpdate
function. This function uses the slider's name
and value
attributes to define the slider’s state.
If you preview the page in your browser, you should see the sliders.
Add Skip Controls
Add the following to your index.html
file:
1<div class="video-player">//...</div>2<div class="controls">3 <div class="progress">4 <div class="progress__filled"></div>5 </div>6 <button class="controls__button toggleButton" title="Toggle Play">►</button>7 <input //... /> <input //... />89 <!-- Add these -->10 <button class="controls__button" data-skip="-10">« 10s</button>11 <button class="controls__button" data-skip="25">10s »</button>12</div>
We added two buttons with data attributes that will be accessed with JavaScript. To make the buttons functional, update your script.js
file with the following:
1//...23// Add this4const skipBtns = document.querySelectorAll("[data-skip]");5//...6function handleSliderUpdate() {7 video[this.name] = this.value;8}910// Add this11function handleSkip() {12 video.currentTime += +this.dataset.skip;13}14//...1516// Add this17skipBtns.forEach((btn) => {18 btn.addEventListener("click", handleSkip);19});2021let mousedown = false;22progress.addEventListener("click", scrub);23progress.addEventListener("mousedown", () => (mousedown = true));24progress.addEventListener("mousemove", (e) => mousedown && scrub(e));25progress.addEventListener("mouseup", () => (mousedown = false));
We selected our buttons using the querySelectorAll
method and added a listener to each button that calls the handleSkip
function on click. This function then updates the video’s current time based on the value specified in the data
attribute.
Add Keyboard Shortcuts
We can take advantage of the default keyboard keydown
event to customize the video player further. To add a keyboard shortcut to the application, add this to the bottom of your script.js
file.
1document.addEventListener("keydown", (e) => {2 if (e.code === "Space") togglePlay();3});
To keep things simple, we added just a keyboard shortcut that toggles play when the space key on the keyboard is pressed. You can log the event object to the console to see other properties.
Find the project here on GitHub.
Conclusion
So far, we've looked at the default HTML5 video element and how to create custom controls for it, but you don't have to stop here. More information is available in the official documentation.
Resources you may find helpful: