Before starting to build a vidoe player, we need to first think about where our video player will be displayed. Obviously, the video player will not be displayed on every url (ex. homepage, search). So the best way to go about this is to create a new JavaScript file on the client folder and display the video player separtately.
On the webpack configuration file, we create an object within the entry property to include all the JavaScript files to be used.
entry {
main: "./src/client/js/main.js",
videoPlayer: "./src/client/js/videoPlayer.js"
}
To avoid overlapping name of entry(which causes an error), we use the [name] keyword on the output.
filename: "js/[name].js"
Additionally, using the block scripts keywords on the base pug.js file, we can freely bring our JavaScript code from the assets folder
block scripts
script(src="/static/js/videoPlayer.js")
To create elements, we can use our pug.js files to write HTML elements while we can use JavaScript to write functions.
To create buttons for a video player, we can simply specify the element and add a selector(just like normal HTML).
For styles, we can modify the SCSS files that are linked to the pug.js files.
button#play Play
button#mute Mute
input(type="range" step="0.1" value=0.5 min="0" max="1")#volume
span#time 00:00/00:00
Next, we move on to write JavaScript code. To make the video player functional, we need to apply event listeners. We first bring all the selectors needed to handle these events and write functions to make all the buttons work.
const video = document.querySelector("video");
const playBtn = document.getElementById("play");
const muteBtn = document.getElementById("mute");
const time = document.getElementById("time");
const volumeRange = document.getElementById("volume");
By referencing the list of event handlers on MDN, we can implement all the buttons on the video player.
const handlePlayClick = (e) => {
// if the video is playing, pause it
if (video.paused) {
video.play();
} else {
video.pause();
}
// else play the video
playBtn.innerText = video.paused ? "Play" : "Pause";
};
const handlePause = () => (playBtn.innerText = "Play");
const handlePlay = () => (playBtn.innerText = "Pause");const handleMute = (e) => {
if (video.muted) {
video.muted = false;
} else {
video.muted = true;
}
muteBtn.innerText = video.muted ? "Unmute" : "Mute";
volumeRange.value = video.muted ? 0 : volumeValue;
};
muteBtn.addEventListener("click", handleMute);
playBtn.addEventListener("click", handlePlayClick);
const handleMute = (e) => {
if (video.muted) {
video.muted = false;
} else {
video.muted = true;
}
muteBtn.innerText = video.muted ? "Unmute" : "Mute";
volumeRange.value = video.muted ? 0 : volumeValue;
};
muteBtn.addEventListener("click", handleMute);
For implementing an event handler for volume in real-time, we need to use the input keyword to detect values.
let volumeValue = 0.5;
video.volume = 0.5;
const handleVolumeChange = (event) => {
const {
target: { value },
} = event;
if (video.muted) {
video.muted = false;
muteBtn.innerText = "Mute";
}
volumeValue = value;
video.volume = value;
};
volumeRange.addEventListener("input", handleVolumeChange);
Aside from the video itself, we need to focus on meta data which is information such as the duration of the video or the dimension of the video.
Just as before, we first start by creating a new pug.js file and link them with video player file.
span#currentTime 00:00
span /
span#totalTime 00:00
const currentTime = document.getElementById("currentTime");
const totalTime = document.getElementById("totalTime");
Next, need to listen to an event using a method called loadedmetadata.
video.addEventListener("loadedmetadata", handleLoadedMetadata);
We then use the event listener to create a new function that calculates and displays the duration of a video.
const handleLoadedMetadata = () => {
totalTime.innerText = Math.floor(video.duration);
};
To reflect and refresh the actual time of the video on the duration, we create a separate function using the timeupdate event.
const handleTimeUpdate = () => {
currentTime.innerText = Math.floor(video.currentTime);
video.addEventListener("timeupdate", handleTimeUpdate);
};
After creating a function that counts the duration of the video, we can format the time in a way so that it counts like an actual timer.
We can do this using a trick with the new date() function where we can multiply the seconds with 1000. We then convert the value of this number to be a string.
const formatTime = (seconds) =>
new Date(seconds * 1000).toISOString().substr(11, 8);
To add a timeline for the duration of the video, we create another range bar which we can control the value of the duration on a video.
To implement this feature, we add another function where the range can spot the exact time on a video that is clicked within the duration. For this, we use the input method.
const handleLoadedMetadata = () => {
totalTime.innerText = formatTime(Math.floor(video.duration));
timeline.max = Math.floor(video.duration);
};
const handleTimeUpdate = () => {
currentTime.innerText = formatTime(Math.floor(video.currentTime));
timeline.value = Math.floor(video.currentTime);
};
const handleTimelineChange = (event) => {
const {
target: { value },
} = event;
video.currentTime = value;
};
timeline.addEventListener("input", handleTimelineChange);
JavaScript has built-in functions that we can apply directly on our video player without the need of creating them from scratch.
To implement a fullscreen feature, we create a button on HTML, bring the selector to JavaScript, and listen to an event using the requestFullscreen() function. As stated before, this function is given to us for free and all we need to do is call it on a function. Using this function will make the video player screen switch to full screen. On the other hand, there is also an exitFullscreen() function which exits fullscreen mode. When using this function, don't forget to call it on a document object.
Optionally, we can also add icons instead of text to make the buttons more interactive.
const handleFullScreen = () => {
const fullscreen = document.fullscreenElement;
if (fullscreen) {
document.exitFullscreen();
fullScreenIcon.classList = "fas fa-expand";
} else {
videoContainer.requestFullscreen();
fullScreenIcon.classList = "fas fa-compress";
}
};
On platforms such as Netflix and Youtube, we can see a control event functionality where the play bar will appear when the mouse is on the video player and disappear when the mouse leaves. We can create this using event handler methods.
Reference code:
let controlsTimeout = null;
let controlsMovementTimeout = null;
const hideControls = () => videoControls.classList.remove("showing");
const handleMouseMove = () => {
if (controlsTimeout) {
clearTimeout(controlsTimeout);
controlsTimeout = null;
}
if (controlsMovementTimeout) {
clearTimeout(controlsMovementTimeout);
controlsMovementTimeout = null;
}
videoControls.classList.add("showing");
controlsMovementTimeout = setTimeout(hideControls, 3000);
};
const handleMouseLeave = () => {
controlsTimeout = setTimeout(hideControls, 3000);
};