A video in the header can be a great addition for creating a certain feel to a website. We will be creating a header video that is mobile friendly and degrades gracefully back to IE8.

What we’re going to be creating

View the demo here

The plan

The video header will have the following functionality:

  • The header will have either an image or a “teaser video”, a shortened down version of the full video, as a background.
  • When the user clicks the “Play video” button, the full video will show up behind it.
  • The video header always follow the videos aspect ratio when resizing to avoid black borders on the side of the iFrame.
  • Graceful degradation for browser without Javascript enabled.
  • Not showing the video on mobile devices to save bandwidth.

The HTML

<div class="header-video">
    <img src="img/masthead.jpg"
         class="header-video__media"
         data-video-URL="https://www.youtube.com/embed/Scxs7L0vhZ4"
         data-teaser="video/teaser-video"
         data-video-width="560"
         data-video-height="315">
    <a href="https://www.youtube.com/embed/Scxs7L0vhZ4" class="header-video__play-trigger" id="header-video__play-trigger">Play video</a>
    <button type="button" class="header-video__close-trigger" id="header-video__close-trigger">Close video</button>
</div>

Our foundation will consist of a single image element with HTML5 data-attributes, which we will use to create an iFrame when the user clicks our link.

  • The data-video-URL is either a Youtube or Vimeo URL.
  • The data-teaser(optional) points to a video file that will be a edited, short-downed version, of the full video in data-video-source. If this isn’t added, the image will be shown instead.
  • The data-video-width and data-video-height will be used to determine the aspect ratio of the video, and later on how large the header will be.
  • The image element will be wrapped inside a container, which will be resized with Javascript.

The Javascript

This is our starting structure for our Javascript:

var HeaderVideo = function(settings) {
    if (settings.element.length === 0) {
        return;
    }
    this.init(settings);
};

HeaderVideo.prototype.init = function(settings) {
    this.$element = $(settings.element);
    this.settings = settings;
    this.videoDetails = this.getVideoDetails();
};

$('.header-video').each(function(i, elem) {
    headerVideo = new HeaderVideo({
      element: elem,
      media: '.header-video__media',
      playTrigger: '.header-video__play-trigger',
      closeTrigger: '.header-video__close-trigger'
    });
});

Lets start with creating a function that stores all our data-* attributes in a object:

HeaderVideo.prototype.getVideoDetails = function() {
    var mediaElement = $(this.settings.media);

    return {
        videoURL: mediaElement.attr('data-video-URL'),
        teaser: mediaElement.attr('data-teaser'),
        videoHeight: mediaElement.attr('data-video-height'),
        videoWidth: mediaElement.attr('data-video-width')
    };
};

Next, we’ll set the height and width of the wrapping container based on the height and width of the iFrame we want to append:

HeaderVideo.prototype.setFluidContainer = function() {
    var element = this.$element;
    element.data('aspectRatio', this.videoDetails.videoHeight / this.videoDetails.videoWidth);

    $(window).resize(function() {
        var windowWidth = $(window).width();
        var windowHeight = $(window).height();

        element.width(Math.ceil(windowWidth));
        element.height(Math.ceil(windowWidth * element.data('aspectRatio'))); //Set the videos aspect ratio, see https://css-tricks.com/fluid-width-youtube-videos/

        if(windowHeight < element.height()) {
            element.width(Math.ceil(windowWidth));
            element.height(Math.ceil(windowHeight));
        }
    }).trigger('resize');
};

The if-statement at the bottom is to prevent the video from being higher than the actual viewport, which will force the user to scroll to see the full video. The downside to this is that some black borders will appear on each side of the video, since the aspect ratio now will be wrong.
Lets create a function that creates our iFrame that will later be appended:

HeaderVideo.prototype.appendIframe = function() {
    var html = '<iframe id="header-video__video-element" src="'+this.videoDetails.videoURL+'?rel=0&hd=1&autohide=1&showinfo=0&autoplay=1&enablejsapi=1&origin=*" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
    this.$element.append(html);
};

We'll also write a function that appends the teaser video to the container, if there's a video available.

HeaderVideo.prototype.appendTeaserVideo = function() {
    var source = this.videoDetails.teaser;
    var html = '<video autoplay="true" loop="true" muted id="header-video__teaser-video" class="header-video__teaser-video"><source src="'+source+'.webm" type="video/mp4"><source src="'+source+'.mp4" type="video/mp4"></video>';
    this.$element.append(html);
};

We'll only be calling this function if the browser supports HTML5-video and isn't a touch based device, with the help of Modernizr. If not, only the image will be shown. This is because we don't want a mobile user to be forced to download the large teaser video, so it's more responsible to give them just the image. They will not be missing out on any content, since the teaser video is just a shortened version of the full one.

That's about it for the Javascript, you can view in its full version over here.

The CSS

The only required CSS for this to work is the following:

.header-video {
  position: relative;
  overflow: hidden;
}

.header-video iframe,
.header-video video {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

.header-video iframe {
  height: 100%;
  width: 100%;
}

.header-video video {
  width: 100%;
}

.header-video__teaser-video {
  width: 100%;
  height: auto; 
}

.header-video__media {
  width: 100%;
  height: auto;
}

All done

There you have it, a fully responsive (and pretty mobile responsible) header video, that still works in older versions of Internet Explorer.
You can see all the code, and fork it on Github. Please let me know in the comments if you have any feedback!


View complete demo