JavaScript-30-Day-11
Tags: javascript, html, webdev, css
Published: 2021-06-11 18:08:15

##<center>Custom Video Player</center>

**[demo](https://cenacrharsh.github.io/JS-30-DAY-11/)**

![ss.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eiepb6rrjq6e948zwc90.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.
```html
<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>
```

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.

```javascript
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();
     }
}
```

We can shorten the code in `if-else` by substituting it with
```javascript
const method = video.paused ? "play" : "pause";
  video[method]();
```


##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.

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

function updateButton() {
  const icon = this.paused ? "▶" : "⏸";
  toggle.textContent = icon;
}
```

##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.
```html
<button data-skip="-10" class="player__button">« 10s</button>
        <button data-skip="25" class="player__button">25s »</button>
```

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

```javascript
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);
}
```

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.
```html
<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"
        />
```
Now we'll select the sliders and update the playback speed and volume.

```javascript
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;
}
```

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

```css
.progress__filled {
  width: 50%;
  background: #ffc600;
  flex: 0;
  flex-basis: 0%;
}
```

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

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

video.addEventListener("timeupdate", handleProgress);

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

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`.

```javascript
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;
}
```

and with this our project for the day was completed.

GitHub repo:
{% github cenacrharsh/JS-30-DAY-11 no-readme %}

###Blog on Day-10 of javascript30
{% link https://dev.to/cenacr007_harsh/javascript-30-day-10-2nl5 %}


###Blog on Day-9 of javascript30
{% link https://dev.to/cenacr007_harsh/javascript-30-day-9-328i %}

###Blog on Day-8 of javascript30
{% link https://dev.to/cenacr007_harsh/javascript-30-day-8-4lhj %}

[Follow me on Twitter](https://twitter.com/cenacr007_harsh)
[Follow me on Linkedin](https://www.linkedin.com/in/kumarharsh22/)

**DEV Profile**

{% user cenacr007_harsh %}

You can also do the challenge at [javascript30](https://javascript30.com/)

Thanks @wesbos , [WesBos](https://github.com/wesbos) to share this with us! 😊💖

**Please comment and let me know your views**
##Thank You!