Skip to content

Commit

Permalink
This refactors the proposed markup (slightly) and picturefill JS to b…
Browse files Browse the repository at this point in the history
…roaden browser support. The included update to README.md explains the changes I'm proposing to the markup pattern (there no changes to elements themselves) to support browsers that formerly did not work, like iOS4, IE browsers versions 6-10, Android browsers 1.x and up. As a result, there aren't any known "unsupported" browsers, in that every browser tested so far at least receives a fallback image - for example, IE6-8 will only see picture source elements that do not use media queries, since it doesn't support them natively (media types are ok though). Fixes scottjehl#5 and Fixes scottjehl#8.
  • Loading branch information
scottjehl committed Mar 22, 2012
1 parent de0443c commit 404c39d
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 20 deletions.
70 changes: 68 additions & 2 deletions README.md
@@ -1,6 +1,6 @@
# Picturefill

A crude polyfill for proposed behavior of the picture element, which does not yet exist, but should. :)
A polyfill for proposed behavior of the picture element, which does not yet exist, but should. :)

* Author: Scott Jehl (c) 2012
* License: MIT/GPLv2
Expand All @@ -12,4 +12,70 @@ A crude polyfill for proposed behavior of the picture element, which does not ye

Demo URL: [http://scottjehl.github.com/picturefill/](http://scottjehl.github.com/picturefill/)

Note: The demo only polyfills picture support for browsers that support CSS3 media queries, but it includes the [matchMedia polyfill](https://github.com/paulirish/matchMedia.js/) for media-query-supporting browsers that don't have matchMedia.
Note: The demo only polyfills `picture` support for browsers that support CSS3 media queries, but it includes (externally) the [matchMedia polyfill](https://github.com/paulirish/matchMedia.js/) which makes matchMedia work in `media-query`-supporting browsers that don't have `matchMedia`, or at least allows media types to be tested in most any browser. `matchMedia` and the `matchMedia` polyfill are not required for `picture` to work, but they are required to support the `media` attributes on `picture` `source` elements.

## Size and delivery

Currently, `picturefill.js` compresses to around 498bytes (~0.5kb), after minify and gzip. To minify, you might try these online tools: [Uglify]:(http://marijnhaverbeke.nl/uglifyjs), [Yahoo Compressor]:(http://refresh-sf.com/yui/), or [Closure Compiler](http://closure-compiler.appspot.com/home). Serve with gzip compression.

`Picturefill` performs a html5-shiv style workaround to get `picture` elements recognized in IE browsers. Because of that, you must reference it from the `head` of your document. If you'd prefer not referencing it from `head`, you'll need to at least call `document.createElement("picture"); document.createElement("source");` somewhere in the head of your document, and then you can load `picturefill.js` whenever you want.

## Markup pattern and explanation

While the [proposed markup for the `picture` element](http://www.w3.org/community/respimg/) is quite simple, enabling its use in browsers that don't yet support it requires a few unfortunate tweaks. The following markup pattern is intended to "bulletproof" existing browser support for `picture` without interfering with future native implementations.

<picture alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia">
<!-- <source src="small.jpg"> -->
<source src="small.jpg">
<!-- <source src="medium.jpg" media="(min-width: 400px)"> -->
<source src="medium.jpg" media="(min-width: 400px)">
<!-- <source src="large.jpg" media="(min-width: 800px)"> -->
<source src="large.jpg" media="(min-width: 800px)">
<!-- Fallback content for non-JS browsers. Same src as the initial source element. -->
<noscript><img src="small.jpg" alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia"></noscript>
</picture>

### Explained...

Notes on the markup above...

* The `picture` element's `alt` attribute is used as alternate text for the generated `img` element.
* The `picture` element can have any number of `source` elements. The above example may contain more than the average situation would call for.
* Each `source` element must have a `src` attribute specifying the image path.
* It's generally a good idea to include one source element with no `media` qualifier, so it'll apply everywhere.
* Each `source` element can have an optional `media` attribute to make it apply in different media settings. Both media types and queries can be used, like any `media` attribute, but support for media queries depends on the browser (unsupporting browsers fail silently).
* The `matchMedia` polyfill (included in `/external`) is necessary to support the `media` attribute across browsers, even in browsers that support media queries, although it is becoming more widely supported in new browsers.
* To ensure `picture` `source` elements are recognized in browsers like iOS4.3, Android 2.x, and IE9, `source` elements should be preceded by a comment containing that `source` element's markup. See the support table for information on which browsers rely on these comments (these browsers remove `source` elements from the DOM at load, so the comments provide a fallback).
* The `noscript` element wraps the fallback image for non-JavaScript environments, and including this wrapper prevents browsers from fetching the fallback image during page load (causing unnecessary overhead). Generally, it's a good idea to reference a small image here, as it's likely to be loaded in older/underpowered mobile devices.


## Support

Picturefill supports a broad range of browsers and devices (there are currently no known unsupported browsers), provided that you stick with the markup conventions provided.

The following table covers some of the major platforms tested so far and their mode of support for the picture element, and picturefill.

<table>
<tr><th>Browser</th> <th>Support Type</th></tr>
<tr><td>Android 1.6 Webkit</td> <td>Full</td></tr>
<tr><td>Android 2.1 Webkit</td> <td>Comment fallbacks used</td></tr>
<tr><td>Android 2.2 Webkit</td> <td>Comment fallbacks used</td></tr>
<tr><td>Android 2.3 Webkit</td> <td>Comment fallbacks used</td></tr>
<tr><td>Android 4.x Webkit</td> <td>Full</td></tr>
<tr><td>iOS 4.3 Safari</td> <td>Comment fallbacks used</td></tr>
<tr><td>iOS 5.0 Safari</td> <td>Full</td></tr>
<tr><td>Opera Mobile</td> <td>Full</td></tr>

<tr><td>Chrome Mac (tested v17)</td> <td>Full</td></tr>
<tr><td>Opera Mac Desktop (tested v11)</td> <td>Full</td></tr>
<tr><td>Firefox Mac Desktop (tested v3.0+)</td> <td>Full</td></tr>
<tr><td>IE 6</td> <td>Full (*no media query support, though)</td></tr>
<tr><td>IE 7 </td> <td>Full (*no media query support, though)</td></tr>
<tr><td>IE 8</td> <td>Full (*no media query support, though)</td></tr>
<tr><td>IE 9</td> <td>Comment fallbacks used</td></tr>
<tr><td>IE 10</td> <td>Full</td></tr>
</tbody>
</table>

...More testing wanted! :)
23 changes: 11 additions & 12 deletions index.html
Expand Up @@ -18,18 +18,17 @@ <h1>Picturefill: A &lt;picture&gt; element polyfill</h1>
<p>For more info: <a href="http://github.com/scottjehl/picturefill">see project home.</a></p>

<picture alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia">
<!-- smallest size first - no @media qualifier -->
<source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_m.jpg">
<!-- medium size - send to viewport widths 400px wide and up -->
<source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5.jpg" media="(min-width: 400px)">
<!-- large size - send to viewport widths 800px wide and up -->
<source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_z.jpg" media="(min-width: 800px)">
<!-- extra large size - send to viewport widths 1000px wide and up -->
<source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_b.jpg" media="(min-width: 1000px)">
<!-- extra large size - send to viewport widths 1200px wide and up -->
<source src="http://farm8.staticflickr.com/7144/6547286841_c6160b34e2_o.jpg" media="(min-width: 1200px)">
<!-- Fallback content for non-JS or non-media-query-supporting browsers. Same img src as the initial, unqualified source element. -->
<noscript><img src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_m.jpg" alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia"></noscript>
<!-- <source src="external/imgs/small.jpg"> -->
<source src="external/imgs/small.jpg">
<!-- <source src="external/imgs/medium.jpg" media="(min-width: 400px)"> -->
<source src="external/imgs/medium.jpg" media="(min-width: 400px)">
<!-- <source src="external/imgs/large.jpg" media="(min-width: 800px)"> -->
<source src="external/imgs/large.jpg" media="(min-width: 800px)">
<!-- <source src="external/imgs/extralarge.jpg" media="(min-width: 1000px)"> -->
<source src="external/imgs/extralarge.jpg" media="(min-width: 1000px)">

<!-- Fallback content for non-JS browsers. Same img src as the initial, unqualified source element. -->
<noscript><img src="external/imgs/small.jpg" alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia"></noscript>
</picture>
</body>
</html>
24 changes: 18 additions & 6 deletions picturefill.js
@@ -1,7 +1,6 @@
/*! Picturefill - Author: Scott Jehl, 2012 | License: MIT/GPLv2 */
/*
Picturefill - a crude polyfill for proposed behavior of the picture element, which does not yet exist, but should. :)
* Author: Scott Jehl, 2012
* License: MIT/GPLv2
Picturefill: A polyfill for proposed behavior of the picture element, which does not yet exist, but should. :)
* Notes:
* For active discussion of the picture element, see http://www.w3.org/community/respimg/
* While this code does work, it is intended to be used only for example purposes until either:
Expand All @@ -25,24 +24,37 @@
for( var i = 0, il = ps.length; i < il; i++ ){
var sources = ps[ i ].getElementsByTagName( "source" ),
matches = [];

// If no sources are found, they're likely erased from the DOM. Try finding them inside comments.
if( !sources.length ){
var picText = ps[ i ].innerHTML,
frag = w.document.createElement( "div" ),
// For IE9, convert the source elements to divs
srcs = picText.replace( /(<)source([^>]+>)/gmi, "$1div$2" ).match( /<div[^>]+>/gmi );

// See if which sources match
frag.innerHTML = srcs.join( "" );
sources = frag.getElementsByTagName( "div" );
}

// See if which sources match
for( var j = 0, jl = sources.length; j < jl; j++ ){
var media = sources[ j ].getAttribute( "media" );
// if there's no media specified, OR w.matchMedia is supported
if( !media || ( w.matchMedia && w.matchMedia( media ).matches ) ){
matches.push( sources[ j ] );
}
}

// Set fallback img element src from that of last matching source element

if( matches.length ){
// Set fallback img element src from that of last matching source element
var picImg = ps[ i ].getElementsByTagName( "img" )[ 0 ];

if( !picImg ){
picImg = w.document.createElement( "img" );
picImg.alt = ps[ i ].getAttribute( "alt" );
ps[ i ].appendChild( picImg );
}

picImg.src = matches.pop().getAttribute( "src" );
}
}
Expand Down

0 comments on commit 404c39d

Please sign in to comment.