I implemented the following interactive video using vanilla JavaScript and HTML5 with with the excuse to play with the <video>
element and the HTMLMediaElement API.
The general idea of this interactive video is to play a video of introduction and present the users with options to control their experience. Click the play icon to see the demo:
Please, change your phone to landscape view!
Note: this demo is just a basic example of an interactive video. If you want a more complete example, you can read Mozilla’s documentation on how to create cross-browsing video players. You can also have a look at the interactive video my friend and colleague Jack Nyaga implemented for Sucuri, it’s pretty awesome!
Note 2: If you’re better at reading code than blog posts, here’s the link to the GitHub gist for this demo.
Intro: Controlling HTML5 videos with JavaScript
Thanks to the <video>
element introduced in HTML5, we can embed a video to a web page with as little code as this:
<video controls>
<source src="/videos/video.mp4" type="video/mp4">
</video>
Without the use of any third-party video player!
The HTMLMediaElement API
The HTMLMediaElement API also allows us to target those elements and control them via JavaScript, which opens our world to many possibilities. Such as creating interactive videos like the one at the beginning of this post.
For example, in order to play a video using JavaScript, all you need to do is target the element and call the load()
& play()
functions.
var video = document.getElementsByTagName("video")[0]; // first video on the page
video.load();
video.play();
The HTMLMediaElement API has many properties and methods available for developers. I used the following list in the previous demo (some of them only while developing):
currentTime
to get the current playback in secondsduration
to get the length of the video in secondsended
to get information on whether the video has finished playingloop
to repeat a video (very useful for interactive menus)playbackRate
to set the rate at which a video is played (time saver for development)preload
to start the loading of a video (indicating it should be ready to be played)load
to set a video to start from the beginning and to start loading the best sourceplay
to play a video
How the interactive video works
Note: The following code is a modified version of my running demo.
HTML markup
<div class="videos">
<div class="active initial closing">
<video autoplay="autoplay" loop>
<source src="/path/video.mp4" type="video/mp4">
</video>
</div>
<div class="category hide">
<video>
<source src="/videos/video-dog.mp4" type="video/mp4">
</video>
</div>
<!-- More videos here -->
<div class="play"><!-- SVG image here --></div>
<div class="menu hide">
<div class="menu-wrapper">
<div class="initial hide">
<h3>Content for first menu</h1>
</div>
<div class="closing hide">
<h3>Content for last menu</h3>
</div>
<div class="options hide">
<ul>
<li><a id="opt-1">Option 1</a></li>
<li><a id="opt-1">Option 2</a></li>
<li><a id="opt-3">Option 3</a></li>
</ul>
</div>
</div>
</div>
</div>
There’s three main sections inside the .videos
container:
- The videos identified with the classes
.initial
,.closing
&.category
. Meaning that adding new videos a “category” is as simple as using the right class on their container element. - The
.play
container to host an svg play button (courtesy of Pixabay). - The
.menu
container that will host as many menus as necessary. In this case I have an.initial
and a.closing
menu, with a shared.options
container.
JavaScript code
The first thing I did was to declare the variables I was going to be using more than once.
var videosContainer = document.getElementsByClassName("videos")[0];
var menu = document.getElementsByClassName("menu")[0];
var menuOptions = menu.getElementsByClassName("options")[0];
Next I created a function to handle the loading and the playing of a given video, and an optional argument to know whether to loop the video or not. I came up with the following:
function playVideo(videoContainer, loop=false) {
var lastVideoContainer = videosContainer.getElementsByClassName("active")[0];
lastVideoContainer.classList.remove("active");
lastVideoContainer.style.display = "none";
videoContainer.style.display = "block";
videoContainer.classList.add("active");
var video = videoContainer.getElementsByTagName("video")[0];
video.preload = "auto";
video.load();
video.play();
video.loop = loop;
return video;
}
Besides the loading and playing functionality of this function, is important to note its use of the .active
class to identify the playing video. At the beginning of the function we retrieve the last element with said class, remove the class and then hide the element. Right after that, the function look for the videoContainer
element, add the .active
class to it and show it as a block
element.
For my demo I also needed to play multiple videos belonging to the same category. So I created the following function:
function showVideos(index, videos) {
if (index < (videos.length - 1)) {
hasNextVideo = true;
} else {
hasNextVideo = false;
}
// If videos are playing, we don't need the menus
menu.style.display = "none";
var video = playVideo(videos[index]);
video.addEventListener("timeupdate", function() {
var currentTime = (this.currentTime / this.duration) * 100;
// Helps to smooth the transition between videos
if (hasNextVideo && currentTime > 70) {
nextVideo = videos[index + 1];
nextVideoTag = nextVideo.getElementsByTagName("video")[0];
nextVideoTag.preload = "auto";
}
})
video.onended = function() {
if (hasNextVideo) {
showVideos(index+1, videos);
} else {
playClosingMenu();
}
}
}
After this, I set a few events handlers to control the flow of the experience through the menus and the options.
Here’s what I did:
var playButton = document.getElementsByClassName("play")[0];
playButton.onclick = function() {
playButton.style.display = "none";
playInitialMenu();
}
var option1 = document.getElementById("option-1");
option1Link.onclick = function() {
showVideos(0, videosContainer.getElementsByClassName("category-opt-1"));
}
// handlers for more options here...
function playInitialMenu() {
playVideo(videosContainer.getElementsByClassName("initial")[0], loop=true);
setTimeout (function () {
menu.style.display = "block";
menu.getElementsByClassName("initial")[0].style.display = "block";
menuOptions.style.display = "block";
}, 1000);
};
There’s also a playClosingMenu()
which is almost exactly as the playInitialMenu()
, with the difference that the playClosingMenu()
function hides the initial menu and displays the closing menu.
And that’s all the code necessary for this demo… Plus a few lines of CSS which I’m not going to address here because I think it goes out of the scope. However, all the code is available on this GitHub gist.
Note: I took some inspiration from this blog post on Sitepoint.
Thanks for reading! I’m on Twitter in case you want to chat!