Dynamically adding text tracks to HTML5 video
In the past I have written on how the track element can be used to add captions and subtitles to HTML5 video, but this, and many other examples around the web, used a static example. But what if you need to load this information dynamically? This is what I’m going to take a quick look at here.
To begin with, there is a Text Track API which does allow you to dynamically add text tracks to video, so let’s look at that first.
The addTextTrack
function allows us to do just that: add a text track.
var video = document.getElementById("video"), track;
video.addEventListener("loadedmetadata", function() {
track = this.addTextTrack("captions", "English", "en");
});
It can take 3 arguments; the kind of track, the track’s label, and the track’s language, all of which are strings.
(Oddly, there’s no removeTextTrack
function).
By default, each track has its mode
set to "hidden"
, so we need to change that to "showing"
to ensure that the track is shown.
track.mode = "showing";
Now that the text track has been added, we now need to add the cues for the text track, i.e. the timestamps for each track and its contents. To do this, we take advantage of the addCue
function which allows us to define and add new cues via the VTTCue
interface.
track.addCue(new VTTCue(0, 12, "[Test]"));
track.addCue(new VTTCue(18.7, 21.5, "This blade has a dark past."));
track.addCue(new VTTCue(22.8, 26.8, "It has shed much innocent blood."));
Each VTTCue is defined with the start time, end time, and text contents, so the first cue will start at 0 seconds, end at 12 seconds, and will display “[Test]”.
But, we need to call addCue
for each and every cue that we want to add to the text track, as, weirdly, there appears to be no way to simply tell the track where to find the WebVTT file that contains all the text tracks. Simply setting track.src = "captions/sintel-en.vtt"
doesn’t appear to work on any browser that I tested, the text tracks are never actually loaded.
This seems a bit odd. If we had used the <track>
element in our HTML to setup our text tracks, we would have simply set the src
attribute to the relevant WebVTT file, and that would be that.
<track kind="captions" label="English" srclang="en" src="captions/sintel-en.vtt">
So, can we achieve this in some other way?
Thankfully yes, but without the Text Track API.
First of all we create a new <track>
element:
video.addEventListener("loadedmetadata", function() {
track = document.createElement("track");
});
Then we initialise it by defining some of its attributes, including, this time, its src
:
video.addEventListener("loadedmetadata", function() {
track = document.createElement("track");
track.kind = "captions";
track.label = "English";
track.srclang = "en";
track.src = "captions/sintel-en.vtt";
});
As mentioned above, a new text track’s default mode
is "hidden"
, but here we don’t set it to "showing"
until the track has actually loaded:
track.addEventListener("load", function() {
this.mode = "showing";
});
Annoyingly, this doesn’t work for Firefox 35.0.1 which currently has all text tracks set to “disabled”, and the only way to override this value is to access the text track via the video itself.
track.addEventListener("load", function() {
this.mode = "showing";
video.textTracks[0].mode = "showing"; // thanks Firefox
});
And finally we need to append this track to our video:
this.appendChild(track);
And putting this all together:
video.addEventListener("loadedmetadata", function() {
track = document.createElement("track");
track.kind = "captions";
track.label = "English";
track.srclang = "en";
track.src = "captions/sintel-en.vtt";
track.addEventListener("load", function() {
this.mode = "showing";
video.textTracks[0].mode = "showing"; // thanks Firefox
});
this.appendChild(track);
});
You can check it in action on the working example.
So while there is a way, it does seem odd that the Text Track API doesn’t have a native method for removing text tracks, nor does it have a way to dynamically load a WebVTT file.
Perhaps it’s time to file some bugs.