Video is an effective medium for learning; instructional designers have been including it in their content for decades. Every known authoring environment allows embedding video. In this article, we’ll look at video from the coding perspective for a better understanding of what’s going on behind the scenes, and the full capabilities of video revealed from marrying video and JavaScript coding.

The code and sample videos are available to download at https://drive.google.com/file/d/1Fvqg3FKxST4GQXQwQkmRzFfnq5Jq94Sy/view?usp=sharing.

About 12 years ago native video was added to JavaScript’s capabilities. This event led to the exponential growth of online video. Before that point, online video had to be played through a proprietary player like Flash or Quicktime. After the addition of the video API to JavaScript and HTML5, a video could be played natively, without the help of a plugin. The JavaScript API also adds a number of commands to give developers fine control over video. Learning authoring tools are directly implementing the JavaScript video API in content.

In order to understand how JavaScript works with the video API, we will build a video player application that plays four videos in succession, and also provides standard user controls and information. The video player build in this article is pictured. (Figure 1.) Our video player has the standard play, pause, and stop buttons, as well as some additional features.

Video player app that we will designFigure 1: We will build a video player app that plays four videos in succession

We’ll start with the HTML for the video player itself. As you know, the HTML is nonfunctional—it provides a scaffolding for the video player itself. Here’s the HTML:

index.html

<!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <title>JavaScript Video Player</title> <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic"> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.css"> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/milligram/1.3.0/milligram.css"> <link rel="stylesheet" href="player.css"/> </head> <body> <div id="container"> <h1>Digital Design School</h1> <video id="myVideo"> <source src="videos/FDD 1.1.mp4"/> </video> <output id="timeOut">Time: 0</output> <output id="vidNum">Video: 1/4</output> <div id="playButtons"> <button id="btnPlay">Play</button><br/> <button id="btnPause">Pause</button><br/> <button id="btnStop">Stop</button> </div> <div id="nextButtons"> <button id="btnNext">Next Video</button> </div> </div> <script src="player.js"></script> </body> </html>

In the head section of the code, there are links to several external style sheets made with link tags. These are required to load the Milligram CSS Framework. Milligram is a minimalist framework that I’m using to style the app. I like Milligram because by merely inserting the links in the head, I can style my entire document.

In the body of the HTML code, you’ll notice a few relevant items. First and most importantly is the video tag. This tag places the video player within the layout in the HTML. Within the video tag, there is a source tag. This tag points to the source of the first video played. The output tags reserve locations in the app user interface to output the elapsed time and the video playing.

Finally, in the code we have four button tags which are used to place the buttons in the bottom part of the user interface. These are the buttons that control the video.

The app also has a separate style sheet. There were a few places where I wanted to add to or alter the styles from Milligram.

player.css

#container{ padding: 10px; } #playButtons, #nextButtons { padding: 10px; border: 1px solid gray; border-radius: 5px; margin-bottom: 20px; } #myVideo{ width: 100%; } #vidNum{ float: right; } button{ width: 100%; }

The styles used here make the application more mobile-friendly. For example, instead of using Milligram’s default width for the buttons, I stretched across the width of the interface. In general, for mobile, bigger buttons are more usable— especially for the fat-fingered among us (like me!)

I also grouped the buttons by placing a solid gray border around the divs that hold them. Other than that, I used padding to give the elements of the interface additional breathing room.

Now we’re on to the heart of the application, the JavaScript. This is where the magic happens!

player.js

const myVideo = document.getElementById('myVideo'); const btnPlay = document.getElementById('btnPlay'); const btnPause = document.getElementById('btnPause'); const btnStop = document.getElementById('btnStop'); const timeOut = document.getElementById('timeOut'); const vidNumOut = document.getElementById('vidNum'); let timer = null; btnPlay.addEventListener('click',vidAction); btnPause.addEventListener('click',vidAction); btnStop.addEventListener('click',vidAction); btnNext.addEventListener('click', nextVideo); myVideo.addEventListener('ended', vidEnded); //Vids const vids = ["FDD 1.1.mp4","FDD 1.2.mp4","FDD 1.3.mp4","FDD 1.4.mp4"]; let vidPlaying = 0; function vidAction(event){ switch(event.target.id){ case "btnPlay": playVideo(); timer = setInterval(update, 100); break; case "btnPause": myVideo.pause(); break; case "btnStop": myVideo.pause(); myVideo.currentTime = 0; break; } } function playVideo(){ myVideo.play(); timer = setInterval(update, 100); } function update(){ timeOut.innerHTML = "Time: " + myTime(myVideo.currentTime) + "/" + myTime(myVideo.duration); } function myTime(time) { var hr = ~~(time / 3600); var min = ~~((time % 3600) / 60); var sec = time % 60; var sec_min = ""; if (hr > 0) { sec_min += "" + hrs + ":" + (min < 10 ? "0" : ""); } sec_min += "" + min + ":" + (sec < 10 ? "0" : ""); sec_min += "" + Math.round(sec); return sec_min; } function vidEnded(){ clearInterval(timer); timeOut.innerHTML = "Timer: 0"; nextVideo(); playVideo(); } function nextVideo(){ if(vidPlaying < 3){ vidPlaying++; } else { vidPlaying = 0; } myVideo.src = "videos/" + vids[vidPlaying]; vidNum.innerHTML = (vidPlaying+1) +"/4"; }

I’m going to review the JavaScript section-by-section so you can determine all the capabilities of the JavaScript video API that we used for this application.

The first section of the JavaScript collects the relevant elements from the HTML and gives them variable references. Since these references won’t change, I made them immutable by declaring them with the keyword cost which declares a constant. I also set up a variable that will refer to the timer, as the user interface has to be constantly updated when a video is playing.

In the next section where the addEventListener() methods are used, I am setting up the buttons to respond to click events. When the buttons are clicked (or touched on a mobile device) they respond by calling a function. The play, pause, and stop buttons call the vidAction() function. The next button calls the nextVideo() function. Finally, I have an event listening to the video player for an ended event, which fires when a video ends and fires the vidEnded() function. This allows the next video to be played automatically when a video ends.

The final part of initialization in the app occurs with the next two lines of code:

//Vids const vids = ["FDD 1.1.mp4","FDD 1.2.mp4","FDD 1.3.mp4","FDD 1.4.mp4"]; let vidPlaying = 0;

The vids array contains the file names of the videos that I want to play. (You actually could add any numbers of videos to this array as long as they were contained in the videos folder within your application.) The vidPlaying variable is keeping track of which video is playing. It’s initially pointed at the first video, which is number 0.

Next, we have the function that responds to the play, pause, or stop buttons in the interface.

function vidAction(event){ switch(event.target.id){ case "btnPlay": playVideo(); timer = setInterval(update, 100); break; case "btnPause": myVideo.pause(); break; case "btnStop": myVideo.pause(); myVideo.currentTime = 0; break; } }

The functions in this code snippet are interacting directly with the video object. There are three methods that are used to control the video playback. When the play button is clicked (btnPlay) the playVideo() function runs, which starts the timer and plays the video. Interestingly, both the pause and stop buttons (btnPause and btnStop respectively) run the pause() function.

For some reason, the JavaScript API does not include a stop() method. To stop, you have to pause() the video and then reset the playhead to the beginning of the video by setting the currentTime property to 0, which is zero seconds.

Onward.

function playVideo(){ myVideo.play(); timer = setInterval(update, 100); } function update(){ timeOut.innerHTML = "Time: " + myTime(myVideo.currentTime) + "/" + myTime(myVideo.duration); }

The playVideo() function has two roles. The first to start the video playback. The second is to start a timer that runs the function called update() every 100 milliseconds. The update() function updates the Time on the display so the user knows how much time has elapsed and how much total time the video is. This is aided by the myTime() function. myTime() is a stock function that converts seconds to minutes and seconds, so the time is in a familiar format for the user.

The vidEnded() function and the nextVideo() function are the last two relevant functions.

function vidEnded(){ clearInterval(timer); timeOut.innerHTML = "Timer: 0"; nextVideo(); playVideo(); } function nextVideo(){ if(vidPlaying < 3){ vidPlaying++; } else { vidPlaying = 0; } myVideo.src = "videos/" + vids[vidPlaying]; vidNum.innerHTML = (vidPlaying+1) +"/4"; }

(You’ll remember that the vidEnded() function fires when a video ends. Detecting when a video ends is what allows us in learning and development to know that a user has watched an entire video. This event is often reported to an LMS.

In our custom code, when the video ends we stop the timer with the clearInteval() function. We reset the time at zero and run the nextVideo() and PlayVideo() function to advance to the next video in the array of videos we discussed at the beginning of the code analysis.

The nextVideo() function is set up so that we don’t advance past the end of the array and return to the first video after the last is played. It also updates the part of the user interface that displays which video is currently playing.

In conclusion

This article was designed to give you a primer on how video works in JavaScript. The code described here certainly could be expanded and modified to provide a custom video experience for learners that isn’t entirely possible with current rapid authoring environments. In fact, very similar code can be used to build an internal “YouTube” environment for just about any company—a worthy project to catalog tribal knowledge in the easy-to-access YouTube format most users prefer.

Expand your coding knowledge

Mark Lassoff will present a full-day workshop, "BYOD: JavaScript Coding for eLearning Pros," on Tuesday, October 22, prior to DevLearn 2019 Conference & Expo, October 23 – 25, in Las Vegas. Registration is now open for both events.