Javascript Icon Get 62% off the JavaScript Master bundle

See the bundle then add to cart and your discount is applied.

0 days
00 hours
00 mins
00 secs

Write JavaScript like a pro. Javascript Icon

Follow the ultimate JavaScript roadmap.

Hacking the HTML5 video element with Suave.js

Suave, for elegant HTML5 videos (how they should have been). Suave was built to re-engineer the unstructured mess the HTML5

HTML5 video is awesome, what’s not awesome is the markup. The semantics and unmodular approach with HTML5 video upset me and I was inspired to fix it. So here’s my idea, which is very modular and works in all browsers supporting HTML5 video.

Table of contents

The problem

HTML5 video is a brilliant invention, cleverly injecting useful pieces of Shadow DOM in for us so we no longer need to code in video controls and other funky buttons. It seems someone had a little too much coffee when thinking about a solution for the markup (don’t get me started on the responsive images element). Why must we configure (manually) a video, what if there are a vast amount of videos on the page?

For those wondering what I’m really digging into, let’s take a cross-browser

<video>
  <source src="video/trailer.mp4" type="video/mp4">
  <source src="video/trailer.ogv" type="video/ogv">
  <source src="video/trailer.webm" type="video/webm">
</video>

I love the naming conventions here, source ‘src’. Source source (they definitely had too much coffee by this point). But seriously, what on earth happened here? HTML5 is meant to be intelligent and in my eyes this is a little dumb. What happens if I suddenly change the file name and/or directories, I’ve then got to change it multiple times… Crazy.

My solution

So here’s where Suave comes in. Thanks to my little script, you no longer have to worry about the above catastrophe and can code an HTML5 video with just one line of code (this is proper valid HTML5, too!):

<video data-src="video/mymovie.{mp4, ogv, webm}"></video>

All you need to do is feed it the file extensions you require for each video inside a data-* attribute, easy. Suave is fully modular as well, call it as many times on the page and it’ll just keep doing its thing. What I also like about this solution is that I’m enhancing HTML5, with HTML5. Of course some people will disagree and say I’m missing a few codecs, lost my mind and am hashing out strange ideas, but my project would be finished ontime and save countless future hours.

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

I’ve been using Grunt.js a lot recently and I love how you can simply include some curly braces to say ‘or this too’, so that’s where the idea came from to simplify an overcomplicated system. This is fully semantic stuff too, if anything this improves the semantics of the

<video src="video/mymovie.mp4"></video>

And that’s where the simplicity of my idea came from. Sure it isn’t how the HTML5 spec intended it, but remember this stuff is still new to everyone and remember this is still a huge work in progress.

JavaScript

For those interested in how Suave works, here’s a break down of the script:

window.suave = ( function ( window, document, undefined ) {

  'use strict';

  /*
   * Constructor function
   */
  var Suave = function ( elem ) {
    this.elem = elem;
  };

  /*
   * Prototypal setup
   */
  Suave.prototype = {

    init : function () {

      var dataAttr = this.elem.getAttribute('data-src');
      var videoSource = dataAttr.match(/^([^]+){/)[1];
      var fileExts = dataAttr.match(/{([^]+)}$/)[1].toString().replace(/\s/g, '').split(',');

      for (var i = 0; i < fileExts.length; i++) {
        var extension = fileExts[i];
        var source = document.createElement('source');
        source.src = videoSource + extension;
        source.type = 'video/' + extension;
        this.elem.appendChild(source);
      }

    }

  };

  /*
   * Initiate the plugin
   */
  [].forEach.call(document.querySelectorAll('video[data-src]'), function (suave) {
    new Suave(suave).init();
  });

})( window, document );

From the top, I create the constructor function, which I pass the current element into (passed in at the bottom loop). This then has some internal Prototype workings that grab the data-src attribute (we’re looking at the init function here).

First I grab the videoSource from the attribute, which uses a RegExp to capture the file path and file name, but not the extension.

Next I grab the file extensions (fileExts) which captures everything inside the curlies {}. From here, I use the .toString() method, which converts the array sent back from .match() to a string (you guessed it), from here, I .replace() any whitespace to get a clean array for adding the file extensions, and then use the .split(‘,’) method to split the string by commas which then returns a new array. I then loop through that array of file extensions and create the right amount of tags, populating them with the necessary src and type attributes.

At the bottom, I then hook into the Array.prototype (but use an empty array shorthand to access this) and loop through all video[data-src] tags, which will hold our Suave.js videos! Inside this loop, I pass in the current element and create a new Suave instance to the current item.

Feedback welcome :)

Learn JavaScript the right way.

The most complete guide to learning JavaScript ever built.
Trusted by 82,951 students.

Todd Motto

with Todd Motto

Google Developer Expert icon Google Developer Expert

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover