This is an exercise from the JS30-challange
So, for todays challenge we'll be making a video player in JavaScript.
For reference, this is the HTML skeleton that I'll be using when selecting my different elements.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>HTML Video Player</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="player">
<video class="player__video viewer" src="video.mp4"></video>
<div class="player__controls">
<div class="progress">
<div class="progress__filled"></div>
</div>
<button class="player__button play-toggle" title="Toggle Play">►</button>
<input
type="range"
name="volume"
class="player__slider"
min="0"
max="1"
step="0.05"
value="1"
/>
<input
type="range"
name="playbackRate"
class="player__slider"
min="0.5"
max="2"
step="0.1"
value="1"
/>
<button data-skip="-15" class="player__button">« 15s</button>
<button data-skip="15" class="player__button">15s »</button>
<button class="player__button fullscreenToggle">[ ]</button>
</div>
</div>
<script src="scripts.js"></script>
</body>
</html>
Challenges & Goals
As this is my first time trying to build something like this, my main challenges and goal will be:
- Understanding the different components needed
- Targeting the correct elements
- Understanding the logic needed for time, rate and progressbar.
For this specific player, the settings available will be:
- Skipping forward/backwards
- Toggling fullscreen
- Setting playback rate
- Changing volume
- Tracking video progress
1. Getting the elements needed
First, I'll make variables for a bunch of elements I need.
const player = document.querySelector(".player");
const video = document.querySelector(".viewer");
const play = document.querySelector(".play-toggle");
const sliders = document.querySelectorAll(".player__slider");
const skipButtons = document.querySelectorAll("[data-skip]");
const progressBar = document.querySelector(".progress__filled");
const progress = document.querySelector(".progress");
const fullScreenToggle = document.querySelector(".fullscreenToggle");
2. Adding Play / Pause
Create the function.
function togglePlay() {
// console.log("Play/Pause");
const method = video.paused ? "play" : "pause";
video[method]();
}
Play the video if it's paused, pause it if it's playing.
In this player, I want the user to be able to click anywhere on the video, as well as using the play/pause button. So I'll be making two event listeners for this.
video.addEventListener("click", togglePlay);
play.addEventListener("click", togglePlay);
And that should do it. Play/Pause should now be working.
3. Adding Volume / Playbackspeed
Create the function and logic.
function handleRangeUpdate() {
video[this.name] = this.value;
}
"this" refers to the element/object this was called on. Take the video attribute and update it's value.
I have two sliders, both of which I selected with one query earlier. So, I'll iterate over them to get a listener on both.
sliders.forEach((slider) =>
slider.addEventListener("change", handleRangeUpdate)
);
And now, changes on the slider will call the handleRangeUpdate function, and update the value accordingly.
4. Adding Skipping/Rewinding 15s
This one is pretty much the same as the last one, just with a bit of logic.
Creating the function
function skip() {
video.currentTime += parseFloat(this.dataset.skip);
}
Takes the value of the buttons dataset.skip(see HTML)and adds it to the current time of the video.
Adding a listener
skipButtons.forEach((button) => button.addEventListener("click", skip));
Now, whichever button i click, depending on the value of the dataset.skip, it will either rewind or skip 10s on click.
5. Adding Fullscreen
This was pretty straight forward, as all the instructions could quickly be found in the MDN docs.
Creating the function
function handleFullscreen() {
if (video.requestFullscreen) {
video.requestFullscreen();
} else if (video.webkitRequestFullscreen) {
video.webkitRequestFullscreen();
} else if (video.msRequestFullscreen) {
video.msRequestFullscreen();
}
}
else if conditions to support other browsers.
Adding a listener
fullScreenToggle.addEventListener("click", handleFullscreen);
And that's it. There's no need to add any conditions for closing fullscreen, as this is already added in the browsers built in fullscreen video player.
6. Updating Progressbar
This one is a tiny bit different as it's an ongoing event as long as the video is playing.
Create the function
function handleProgress() {
const percent = (video.currentTime / video.duration) * 100;
progressBar.style.flexBasis = `${percent}%`;
}
We calculate the percent of the video, and set the flexBasis(CSS) value to the calculated percent.
Add a listener
video.addEventListener("timeupdate", handleProgress);
Listens for updates on the time of the video(automatically), and runs the function.
And that's it. Now the progressbar's css will update according to the video's time.
7. Set time manually in progressbar.
To achieve this, we'll use the positioning of the mouseclick comparative to the width of the Progress to get a value.
function time(e) {
const setTime = (e.offsetX / progress.offsetWidth) * video.duration;
video.currentTime = setTime;
}
Take the position where I clicked, divided by the width of the progressbar and multiply with duration to get a value.
And then the listener
progress.addEventListener("click", scrub);
Now, to take this functionality up a notch, I also added the option to drag the progressbar up/down - like you usually can do on videoplayers.
let mousedown = false;
progress.addEventListener("mousemove", (e) => mousedown && time(e));
progress.addEventListener("mousedown", () => (mousedown = true));
progress.addEventListener("mouseup", () => (mousedown = false));
Check if mousedown is true and then update the time according to the event(where your mouse is)
Results
Thoughts
I have to say it was way less complicated than I thought it would've been. The key points that I felt were either new or interesting were:
- The various types of listeners you can use.
- Using HTML5's media element.
Definitely a fun exercise and I'm excited to see what's next in the JS30 challenges.
Top comments (0)