DEV Community

Cover image for JavaScript-30-Day-11
KUMAR HARSH
KUMAR HARSH

Posted on • Updated on

JavaScript-30-Day-11

Custom Video Player

demo

ss.png

On day 11 of JS-30 we made a custom video player in HTML5 and using JavaScript and CSS (for styling the control buttons) added a lot of features and functionalities to the video player.

In this lesson I gained a better understanding of how video, and by extension audio, elements can be manipulated both in style and functionality.

So let's get right into it.

Here is the html we had by default so you don't get confused as to which element has what class.

<div class="player">
      <video class="player__video viewer" src="652333414.mp4"></video>

      <div class="player__controls">
        <div class="progress">
          <div class="progress__filled"></div>
        </div>
        <button class="player__button 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="-10" class="player__button">« 10s</button>
        <button data-skip="25" class="player__button">25s »</button>
      </div>
    </div>
Enter fullscreen mode Exit fullscreen mode

The buttons and sliders were styled by default now we'll add functionality to them using JavaScript.

Before we start one advice, Always turnoff autoplay on video and audio unless the user is expecting it.

Toggle Play

First we'll add a togglePlay function and when this is called it will either going to play or pause the video. We want to play/pause when user either clicks the play/pause button or clicks on the screen so we'll add click event listeners to both the button and the screen.

const video = player.querySelector(".viewer");
const toggle = player.querySelector(".toggle");

video.addEventListener("click", togglePlay);
toggle.addEventListener("click", togglePlay);

function togglePlay() {
     if (video.paused) {
       video.play();
     } else {
       video.pause();
     }
}
Enter fullscreen mode Exit fullscreen mode

We can shorten the code in if-else by substituting it with

const method = video.paused ? "play" : "pause";
  video[method]();
Enter fullscreen mode Exit fullscreen mode

Updating play/pause button

Now that we have added play/pause functionality we should also update the play/pause button along with it.

Now we would now do that inside the togglePlay function because videos can be paused by other ways as well like a plugin or if a pop up opens so what we will do is we'll listen for the video whenever it is paused. So then whatever may be the reason of the video stopping we'll update our buttons.

video.addEventListener("play", updateButton);
video.addEventListener("pause", updateButton);

function updateButton() {
  const icon = this.paused ? "" : "";
  toggle.textContent = icon;
}
Enter fullscreen mode Exit fullscreen mode

Skip Buttons

We have added 2 skip buttons, one takes the video backward by 10sec and the other takes the video forward by 25sec, and we added that in the elements html using data- property.

<button data-skip="-10" class="player__button">« 10s</button>
        <button data-skip="25" class="player__button">25s »</button>
Enter fullscreen mode Exit fullscreen mode

Now we'll make use of the value stored in data attribute.

const video = player.querySelector(".viewer");

const skipButtons = player.querySelectorAll("[data-skip]");

skipButtons.forEach((button) => button.addEventListener("click", skip));

function skip() {
  //   console.log(this.dataset);
  //   console.log(this.dataset.skip);
  video.currentTime += parseFloat(this.dataset.skip);
}
Enter fullscreen mode Exit fullscreen mode

Here you console and see that this.dataset contains an object that has the skip value in it, so we'll use that value and update our video's currentime.

Range Sliders

We have added 2 input elements of type range, one for volume and the other for playback speed.

We intentionally added a name attribute with the same name as the property a video has that is volume and playbackRate so that later we can make use of that name.

<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"
        />
Enter fullscreen mode Exit fullscreen mode

Now we'll select the sliders and update the playback speed and volume.

const ranges = player.querySelectorAll(".player__slider");

ranges.forEach((range) => range.addEventListener("change", handleRangeUpdate));
ranges.forEach((range) =>
  range.addEventListener("mousemove", handleRangeUpdate)
);

function handleRangeUpdate() {
  //   console.log(this.name);
  //   console.log(this.value);
  video[this.name] = this.value;
}
Enter fullscreen mode Exit fullscreen mode

Here this.name contains the name of the property and this.value contains the value to which we want to update the property to.

Progress Bar

We want our progress bar to update in real time and also if a user clicks on it or drags it the video playback should update accordingly.

Also we do not want the handleProgress() fucntion to run every second, rather we'll listen for an event called timeupdate which is fired everytime the timestamp changes.

Another challenge is how are we going to make the progress bar increase/decrease according to video current time. We are going to make a percentage because that is how we implemented it in CSS using flex-basis, we initially start with a 0% and we will be updating that flex basis value (sort of like updating the width of the progress bar) and it will correspond with the video progress

.progress__filled {
  width: 50%;
  background: #ffc600;
  flex: 0;
  flex-basis: 0%;
}
Enter fullscreen mode Exit fullscreen mode

Now inside our handleProgress() function we will calculate the pecentage.

const video = player.querySelector(".viewer");

video.addEventListener("timeupdate", handleProgress);

function handleProgress() {
  const percent = (video.currentTime / video.duration) * 100;
  progressBar.style.flexBasis = `${percent}%`;
}
Enter fullscreen mode Exit fullscreen mode

The currentTime and duration are properties on video.

Scrub

We want to add the functionality where someone clicks/holds & drags on the progress bar and video is adjusted accordingly.

To make sure user has clicked the mouse while dragging we maintain flag and update it accordingly to the mouse click by using mousedown and mouseup event listeners.

Inside the function we can console log and see the mouse event has properties of which we are going to use offsetX which tells us exactly the user clicked and the values are relative to the progress bar, also offsetWidth tells the exact width of the progress bar, so by dividing them we get the percentage our video playback should be so after multiplying it with the video.duration we can obtain the time where our video playback should be and hence we update the video.currentTime.

const progress = player.querySelector(".progress");

let mousedown = false;
progress.addEventListener("click", scrub);
progress.addEventListener("mousemove", (e) => {
   if (mousedown) {
     scrub(e);
   }
 });
progress.addEventListener("mousedown", () => (mousedown = true));
progress.addEventListener("mouseup", () => (mousedown = false));

function scrub(e) {
  //   console.log(e);
  const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration;
  video.currentTime = scrubTime;
}
Enter fullscreen mode Exit fullscreen mode

and with this our project for the day was completed.

GitHub repo:

Blog on Day-10 of javascript30

Blog on Day-9 of javascript30

Blog on Day-8 of javascript30

Follow me on Twitter
Follow me on Linkedin

DEV Profile

You can also do the challenge at javascript30

Thanks @wesbos , WesBos to share this with us! 😊💖

Please comment and let me know your views

Thank You!

Top comments (0)