Sometimes you encounter something that'll surprise you, and yesterday was one of those days: Chrome does not support inline media queries on the source tag within videotag. (you can test it here) Worse, plain media queries will not stop multiple videos from loading, which effectively doubles your data, so it requires a JS solution. CSSTricks has an article from 2012 using jQuery, but there's no follow up and I wasn't that enthralled. I saw, thenewcode: Make HTML5 Video Adaptive With Inline Media Queries but it fails to mention Chrome's refusal to support it.

Javascript to the rescue

First, I wanted to prevent any request to be made, so I created an empty video tag with my two videos as attributes. Easy right? Now that all major browsers support MPEG4, I could safely assume the only legacy users are IE and Safari as the browsers are tied to OS updates, whereas Chrome and FireFox are not thus very few users would not be using a recent browser. Safari and IE both support MPEG4. There's not a good reason for me to want to support WebM.

<video
  preload="auto" autoplay="" loop="" muted="" playsinline=""
  data-desktop-vid="https://iconaircraft.s3.amazonaws.com/ICON_Web+4.0_Loop_16x9_DRAFT190723_26sec+3700.mp4"
  data-mobile-vid="https://iconaircraft.s3.amazonaws.com/ICON_Web+4.0_Loop_1x1_DRAFT190723_26sec-mobile.mp4"
  >
</video>
  

I didn't want to rely on any framework, jQuery document ready meant the JS wouldn't fire until the rest of the page loaded, and es6 meant leaving out old browsers. Thus, I'm limited to ES5.

First, I needed to get all the videos on the page. This creates a variable that contains an array of objects, even if only one is found on the entire page.

//get all vids
var video =  document.querySelectorAll('video')

Next, I needed to create a source for the video tag. The source tag needs an src and type. After that we need to append the newly created DOM element back to an element. This function doesn't need to know how many videos are on the page or what the screen size. It just will return a source to a video tag.

//add source to video tag
function addSourceToVideo(element, src) {
    var source = document.createElement('source');
    source.src = src;
    source.type = 'video/mp4';
    element.appendChild(source);
}

Next is where the logic happens. If a screen size over a predetermined value, I will load the desktop or mobile version. Since I have two data-attributes to work off of depending on the screen size depends on which one I want to use. If the screen is above a certain size, it will grab the desktop version instead of the mobile version, to feed to addSourceToVideo. Easy enough, right?

//determine screen size and select mobile or desktop vid
function whichSizeVideo(element, src) {
    var windowWidth = window.innerWidth ? window.innerWidth : $(window).width();
    if (windowWidth > 800 ) {
        addSourceToVideo( element, src.dataset.desktopVid);
    } else {
        addSourceToVideo(element, src.dataset.mobileVid);
    }
}

Now that we've written code to determine write sources to empty video tags, it needs to init and be able to handle multiple videos. Remember our array of objects? It's time to use it. There's no point of running the code if there aren't any videos on the page, so we need to check to see if the var videos contains any data. If it does, then we need to loop over our array and return an individual video in case we have multiple videos on our page.

//init only if page has videos
function videoSize() {
  if (video !== undefined) {
    video.forEach(function(element, index) {
            whichSizeVideo(
                element, //element
                element  //src locations
            );
    });
  }
}
videoSize();

Notably, you could tie the above code to a resize event incase a user resizes the window and have it trigger videoSize. I chose not not to for simplicity. You can see the working version of the above code, on CodePen. I didn't embed it in this post, so those using a slower connection aren't being hit with 30 MB of video data. Place this script inlne or as seperate file below your videos, but before the rest of your JS payload for maximum performance.