Experimenting with FlexSlider and and HTML5 Video

I thought I would be fancy and try to stick some HTML5 video into a FlexSlider carousel without using any fancy video scripts. It mostly worked, but of course there are issues. And yes, I eventually used a fancy 3rd party script.

Downtown Portland

I took a few short videos in Portland so I could play with video formats in HTML5.

I won’t go into a crazy amount of detail about how HTML5 works because this isn’t a tutorial. But if you’ve read tutorials, then you know that you start with mp4, fall back to ogg for Firefox, and then fall back to YouTube or Flash. I decided to do this with as little JavaScript as possible, so I didn’t use VideoJS at first. I used Modernizr, jQuery, FlexSlider and a bit of custom code.

First, I just put together a simple, default slider. You can check out the demo here. Then I turned off the slideshow property so it would only slide if the user tells it to do so. That demo is here. Then I added some video. That slider demo is here. Finally, I decided that I had tinkered enough and it was time to use a 3rd party resource. I added VideoJS for the fourth demo slider.

Some Things to Notice

Swiping works just fine on mobile if we’re using images, but everything gets sticky when video comes in. I was still able to use the slider controls in Chrome and Firefox, but I wasn’t able to swipe it along when video was included. When images were mixed with video, I could swipe as long as the current slide was an image. I’m sure this could be corrected with the right JavaScript, but doing so was beyond the scope of this test.

How It’s Put Together

You can see by checking the source code that I didn’t do very much with FlexSlider besides adjust the properties. You can see the docs for that on their site. The only things I did that might be notable is converting my mp4 videos to ogg format. I used this site to get the job done, but fair warning: it increased the file size. I tried other converters, but they either chopped the sides from my widescreen format or left me with a blurry video. Simple Theora Encoder was an option at first, but it doesn’t seem to like working with mp4 files. The screenshots use avi files, so maybe that’s all it converts from. In the end, I stuck with the larger file sizes and moved on. It’s no small matter, though. Two of the videos only had minor increases, but the other one went from 10.3mb to 20.5mb!

So I put the sources into a video element and let them fall back naturally. If you want a good code example, you can find one on a nice post called Video for Everybody. He does a pretty good job of explaining things. The main difference in my code is that I used a Youtube embed instead of a Flash player as my final fallback.

before: function(){ //Callback: function(slider) - Fires asynchronously with each slider animation
	// Running through all of the video elements and telling them to pause
	// This will only work with  HTML5 video. YouTube requires its own solution.
	$('.slides > li > video').each( function(i) {
		$(this).get(0).pause();
		// Note the get(0) part. That turns an array into a sinle element,
		// which we need in order to call the pause() method.
	});
},

As an added bonus, I added an extra bit to one of FlexSlider’s callback functions. It finds all of the video elements in the slider and tells them to pause. This works in a desktop browser, but again I had trouble with the iPad. Most likely it requires some detection before giving the pause command, maybe some listeners, etc. But again, that’s more than I wanted to tackle out of simple curiosity.

VideoJS Makes Things Easier

For the last demo, I added VideoJS. It makes everything simpler, but I found that with multiple videos I had to make sure I initialized each one. I chose to do that by looping through them and just calling their ready method. This also allowed me to set the myPlayer variable to the first video.

$('.slides > li > video').each( function(i) {
	currentVidID = $(this).attr('id');
	console.log('ready(): currentVidID = ' + currentVidID);
	if (i==0) {
		videojs( currentVidID ).ready(function(){
			console.log("VideoJS is ready. First player.");
			myPlayer = this; // Initialized to the first player.
		});
	} else {
		videojs( currentVidID ).ready(function(){
			console.log("VideoJS is ready. Next player.");
		});
	}
});

After that, I had to drop a pause command into FlexSlider’s before property. It sets the myPlayer variable to the current video and pauses everything else. Of course, this is a bit redundant the first time it fires because in the beginning it’s already set to the first video player. But with a small demo like this, it’s not a big deal to reassign it anyway. When the user advances to the next video, the one being left behind is paused. This allows us to move on without having to listen to a now invisible player.

// Called by FlexSlider when advancing to the next slide.
currentVidID = $('.flex-active-slide > .video-js').first().attr('id');
myPlayer = videojs(currentVidID); // Setting to the currently viewed player. We might not be on the first video when this is called.
if(!myPlayer.paused()) {
	myPlayer.pause();
	console.log(currentVidID + " is supposed to pause now.");
}

Possible Improvements

If I wanted to take this further, there are several things I could do. First, I could develop this last test into something that can be reused for any video anywhere on a site. I could build a small abstraction layer to allow another developer to just drop in some simple markup and one set of parameters to run both FlexSlider and VideoJS. And of course it would have to play nicely whether a slide has a video or and image or anything else a developer might want to toss in there. Another improvement would be to make the players properly responsive. Others have already solved this problem with JavaScript, so I would most likely do the same. However, if I’m going to do something on resize() then I would wan to throttle it so it doesn’t execute a boatload of times. For this kind of thing, I would defer to the wisdom of Ben Alman.

Neat stuff all around! I don’t have any practical use for it at the moment since I normally just use YouTube, but it’s likely that one of these bits will come in very handy in the future. Most likely when I least expect it.

  • Ron

    Good stuff!
    When you remove the mp4 and ogg, does the youtube video play? Didn’t work for me.

  • On which one? It shouldn’t matter, at least not with video.js. Going with regular HTML5 video can get tricky though. In the end, I stopped fighting it and just went with the 3rd-party stuff.

  • Ron

    Thanks for the reply, Brian!
    I used your 4th (last) demo and removed the mp4 and ogg files because I just wanted to show the Youtube videos but the youtube videos don’t render. Would it be possible for you to create a 5th demo with just the YouTube videos? Thanks!

  • Oh, I see what you mean. The 4th example just uses HTML5 video without a YouTube fallback, so after removing the source tags you were left with just the poster image. I tried to put YouTube in there per the instructions on the VideoJS docs, but no joy. No worries, though. Someone already had this problem and solved it. Here’s the Stack Overflow page where I saw the solution. (The page title mentions Ruby on Rails, but it’s really a JavaScript question.) You’ll need to add this: https://github.com/eXon/videojs-youtube. Then you should be in business.

  • Pingback: Brian Cribb | Return of the Old Thing to the New Now - Brian Cribb()