Wednesday, January 9, 2013

Scalable IFrame Video

Scalable Video

It's easy to scale <img> and <video> elements with window width. The "max-height: 100% trick" makes this task straightforward:

img.scalable, video.scalable {
  max-width: 100%;
}

Width (%):

It's not really a trick - it uses features that have been around in HTML more or less forever. If you give an image height and width attributes, it will scale to that size, even if that distorts the aspect ratio. However, if you give it only one of the two values, the other will scale to match, keeping the original aspect ratio. This behavior carries over to measurements provided via CSS rather than as tag attributes.

When you use max-width: 100%, and the container element becomes narrower than the image's or video's natural size, it becomes a similar constraint on the width, and the height scales to match. (One thing that this means is that you're not necessarily limited to using 100%. You can set max-width to whatever percentage you wish.)

But, when the video is embedded in an <iframe> element, in particular an iframe that is from a different server, things become more difficult. You can't get inside a foreign iframe to stylize it's elements, and iframes don't have a concept of a natural aspect ratio.

Yet another issue is that the space needed by a video won't actually have a constant aspect ratio if it has controls - the controls have a fixed height regardless of the width.

I have seen solutions that rely on padding-bottom using a percentage, taking advantage of the fact that vertical padding and margins expressed in percentages are percentages of the element's width, not its height. My experience has been that this usually requires a bit of tinkering to get right, and adds some not-so-intuitive markup. I would like to propose what I believe is a simpler and more robust approach. While it does add some markup, I think that it's easier to understand what it does.

The video embedded here uses a that approach. Rather than mess around trying to break my template styles in order that the column be resizable, I added a text box that you can use to enter a width (in percent of the column width) for the video iframe. If you want to see a more dynamic, window-width-based version, see the Scalable Iframe Video page at my site.

Mocking the Video

Not, I'm not going to make fun of it - I'm going to create a mockup using transparent GIF images. The total space occupied by the video will usually consist of two pieces: the video itself, which has a fixed aspect ratio, and a controller, which will have a fixed height. The combination of those elements will therefore not have a fixed aspect ratio.

But, we can build a div using two transparent images with the expected sizes. In the model I tested, the video has a 4/3 aspect ratio, so I used a 420 * 315 pixel image as a mock. I also used a 420 * 36 pixel image to mock the controls. The key aspect of the associated CSS is that the mock image for the video itself has max-width: 100% and no height specification, but the controls image has the height fixed at 36px.

I put both of these in in a container div which in turn sits in a larger, outer container div, along with the iframe for the video.

The combined height of the two mock images determines the height of their immediate container. The width of that container, however, is determined by its parent.

The parent that contains the assembled mock div and the iframe has position:relative;, in order to serve as an offset parent to its children. The mock div is unpositioned, so that it sits at 0,0 by default, and also serves to determine the height of the container. The iframe, however is absolutely positioned at 0,0, and has height and width of 100%, which is what makes it assume the aspect ratio of the container.

<div class="vidFrame">
  <div class="vid-box">
    <img src="vid-mock-3x4.gif" class="vid-mock">
    <img src="vid-controls.gif" class="vid-controls">
  </div>
  <iframe width="420" height="315"
    src="http://www.youtube.com/embed/0WyhMV81dro"
    frameborder="0"
    allowfullscreen></iframe>
</div>
.vid-box {
  width: 420px;
  overflow: hidden;
  max-width: 100%;
}
.vid-box img {
  display: block;
  width: 100%;
}
.vid-mock {
  max-width: 100%;
}
.vid-controls {
  height: 36px;
  max-width: 100%;
}
.vidFrame {
  position: relative;
  width: 420px;
  max-width: 100%;
}
.vidFrame iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}