diff --git a/bower.json b/bower.json index fb4fbfc..4dfc7d0 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "picturePolyfill", - "version": "1.0.0", + "version": "2.0.0", "main": "picturePolyfill.js", "ignore": [ ".idea", diff --git a/index.html b/index.html index e35582b..ad733f6 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ body { margin: 0; padding: 0; text-align: center; } .intro { padding: 0 16px; } .outro { padding: 1px 16px; clear: both; } - .element a, .element span, .element img { display: block; } + .element a, .element span, .element img { display: block; } img { width: 100%; height: auto; border: 0; } .element h2 { padding: 0 16px; } .element:hover h2 { text-decoration: underline; } @@ -21,45 +21,42 @@

Responsive images using PicturePolyfill - Demo

-

The following link contains two responsive images created using a <span> element, inline JSON (instead of <source> markup), and the picturePolyfill script.

+

The following link contains two responsive images created using a real <picture> element, and the picturePolyfill script.

With HD (Retina) display support

- + + + + + - +

Without HD (Retina) display support

- + + + + + - +
+
- \ No newline at end of file diff --git a/picturePolyfill.js b/picturePolyfill.js index 1ad52e6..c7b2d0e 100644 --- a/picturePolyfill.js +++ b/picturePolyfill.js @@ -5,90 +5,154 @@ "use strict"; var timerId, - pixelRatio = Math.ceil(w.devicePixelRatio || 1), // The pixel density (2 is for HD aka Retina displays) - mediaQueriesSupported = w.matchMedia && w.matchMedia("only all") !== null && w.matchMedia("only all").matches; + pixelRatio, + mediaQueriesSupported, + browserCanAppendImagesToPictures; + /** + * Detects if browser can append images to pictures + * @returns {boolean} + */ + function detectIfBrowserCanAppendImagesToPictures() { + var newImgElement = document.createElement('img'), + theFirstPictureElement = document.getElementsByTagName("picture")[0]; + + try { + if (theFirstPictureElement) { + theFirstPictureElement.appendChild(newImgElement); + theFirstPictureElement.removeChild(newImgElement); + } + return true; + } + catch(e) { + return false; + } + } /** - * Returns the proper src from the srcSet property - * If arrayOrString is a string, returns it - * else get the first valid element from passed position to the left - * @param arrayOrString - * @param position - * @returns {*} + * Replaces the existing picture element with another picture element containing an image with the imgSrc source + * @param picture + * @param imgSrc + * @param imgAlt */ + function replacePictureWithPictureAndImg(picture, imgSrc, imgAlt) { + var newImage = document.createElement("img"), + newPicture = document.createElement("picture"); + newImage.setAttribute('src', imgSrc); + newImage.setAttribute('alt', imgAlt); + newPicture.appendChild(newImage); + picture.parentNode.replaceChild(newPicture, picture); + } - function getSrcFromSrcSet(arrayOrString, position) { - if (typeof arrayOrString === 'string') { - return arrayOrString; + /** + * Returns a hash density > sourceSet + * @param srcSetAttribute + * @returns {{}} + */ + function getSrcSetHash(srcSetAttribute) { + var srcSetElement, + source, + density, + hash = {}, + srcSetElements = srcSetAttribute.split(','); + + for (var i=0, len=srcSetElements.length; i0) { + return hash; + } + + /** + * Returns the proper src from the srcSet property + * Get the first valid element from passed position to the left + * @param srcSetArray + * @param position + * @returns {string} + */ + function getSrcFromSrcSetArray(srcSetArray, position) { + var ret; + do { + ret = srcSetArray[position+'x']; position-=1; } - return arrayOrString[position]; + while (ret===undefined && position>0); + return ret; } - /** * Loop through every element of the dataPicture array, check if the media query applies and, * if so, get the src element from the srcSet property based depending on pixel ratio - * @param dataPicture + * @param dataPicture {element} * @returns {string} */ - function getSrcAttributeFromData(dataPicture) { - var media, matchedSrc; + var media, + matchedSrc; for (var i=0, len=dataPicture.length; i0)b-=1;return a[b]}function f(b){var d,f;for(var g=0,h=b.length;g0);return c}function j(b){var d,e;for(var f=0,g=b.length;fWithout HD (Retina) display support (function(){ - var picture; - function replacePictureWithPictureWithImg(picture, imgSrc) { var newImage = document.createElement("img"), newPicture = document.createElement("picture"); @@ -65,26 +63,26 @@

Without HD (Retina) display support

} function updateImgSrcOrReplacePicture(picture, imgSrc) { - var imgs = picture.getElementsByTagName("img"); - if (imgs.length === 0) { + var foundImages = picture.getElementsByTagName("img"); + if (foundImages.length === 0) { replacePictureWithPictureWithImg(picture, imgSrc); } else { - imgs[0].setAttribute("img", imgSrc); + foundImages[0].setAttribute("src", imgSrc); } } - var appendImageIsAllowed, pictures; + var picture, pictures; pictures = document.getElementsByTagName("picture"); for (var i=0, len=pictures.length; i - - - - Responsive images with picturePolyfill - by Andrea Verlicchi - - - - -
-
-

Responsive images using PicturePolyfill - Demo

-

The following link contains two responsive images created using a <span> element, inline JSON (instead of <source> markup), and the picturePolyfill script.

-
-
-

With HD (Retina) display support

- - - - - - - - -
-
-

Without HD (Retina) display support

- - - - - - - - -
- -
-

Real time server side scaling is provided by Pixtulate.

-
-
- - - - \ No newline at end of file diff --git a/version1/README.md b/version1/README.md new file mode 100644 index 0000000..326d778 --- /dev/null +++ b/version1/README.md @@ -0,0 +1,187 @@ +# picturePolyfill +A Responsive Images approach that you can use today that mimics the [proposed picture element](http://www.w3.org/TR/2013/WD-html-picture-element-20130226/) using a single `span` (for safety sake) with a JSON notation of `source`, with `media` and `srcset` attributes. + +* Author: Andrea Verlicchi (c) 2014 +* License: MIT/GPLv2 + +### **[DEMO](http://verlok.github.io/picturePolyfill/)** + +**Note:** picturePolyfill works best in browsers that support CSS3 media queries. The demo page references (externally) the [matchMedia polyfill](https://github.com/paulirish/matchMedia.js/) which makes matchMedia work in `media-query`-supporting browsers that don't support `matchMedia`. The `matchMedia` polyfill is not required for `picturePolyfill` to work, but it's required to support the `media` property specified in the data-picture attribute. In non-media query-supporting browsers, the `matchMedia` polyfill will allow for querying native media types, such as `screen`, `print`, etc. + +## picturePolyfill advantages + +PicturePolyfill is fast and easy to use because: + +* **loading performance**: it serves only one image, no other double HTTP requests are made +* **computing performance**: it doesn't execute while a smooth (animated or manually dragged) browser resize is in progress, avoiding useless DOM parsing and HTTP requests to mid-breakpoints images that the user might not need +* **support to HD (Retina) displays** easily made (no need to prefix HD media queries with the `-webkit-` prefix) +* **small html markup** thanks to the possibility to specify multiple values in the `srcset` property + +### Differences with picturefill + +picturePolyfill is better than picturefill because: + +* it gives you the **`srcset` attribute**, which gives you easier support for retina displays. picturefill requires more lines of markup code (and the `-webkit-` prefix) to support different media queries. +* it makes you **choose a default image** that you want to show on Internet Explorer 8. picturefill always serves the smaller one +* it's **faster**, as it relies on a `JSON` object, that is much faster to be parsed then many span elements required by picturefill (see "performance" section on this readme) + +## Markup pattern and explanation + +### With HD (Retina) images support + +To support HD (Retina) images, mark up your responsive images like this. + +```html + + + +``` + +### Without HD (Retina) support + +If you don't need to support HD (Retina) images, you can mark up your responsive images like this. + +```html + + + +``` + +### The `data-picture` attribute array + +The `data-picture` attribute accepts an array. In each element, it accepts: +* `media`: any and all CSS3 media queries—such as `min-width` or `max-width` +* `srcset`: the image URL (string) at the corresponding `media`, or an array of image URLs. To support only standard displays, just pass in a string. To support HD (Retina) displays, pass an array of values: the first value for standard displays, the second value for HD displays (Retina; double density), and more for triple and quad density. +* `standard`: a boolean value, `true` if you want this to be the image picked by browsers without media query support (like IE 8 or below). If srcset is an array, these browser will always load the first `srcset` element. + +**Note:** As the `data-picture` attribute array is read from left to right, the array elements with `media` property set to `min-width` must be placed in increasing `min-width` order, e.g. 321px, 481px, 769px, etc. + +### Notes on the markup above... + +* The `data-picture` attribute must contain a JSON array which can contain any number of elements. The above example may contain more than the average situation may call for. I recommend to generate this array using a helper on a server side language (like PHP or similar). +* The `span[data-picture]` element's `data-alt` attribute is used as alternate text for the `img` element that picturePolyfill generates upon a successful. +* It's generally a good idea to leave one element of the `data-picture` array with no `media` qualifier, so it'll apply everywhere - typically a mobile-optimized image is ideal here. +* Each element of the `data-picture` array can have an optional `media` attribute to make it apply in specific media settings. Both media types and queries can be used, like a native `media` attribute, but support for media _queries_ depends on the browser (unsupporting browsers fail silently). +* The `noscript` element wraps the fallback image for non-JavaScript environments and search engines, and including this wrapper prevents browsers from fetching the fallback image during page load (causing unnecessary overhead). + +### About the real `picture` element + +Some developers are [wondering](http://www.linkedin.com/groupItem?view=&gid=2071438&type=member&item=5846510553693986816&commentID=5848302870645993472): **will I have to re-code my HTML** when the real `picture` element will be standard and supported? + +Please note that there's a version of picturePolyfill, under the [usingPictureMarkup](https://github.com/verlok/picturePolyfill/tree/master/usingPictureMarkup) folder, which makes it possible to **use the real `picture` + `source` tags today**, but this version is supporting Internet Explorer 10 and above (no support for versions 8 and 9). + +If you need to support IE 8 and 9 the answer is yes, recoding will be necessary when the real `picture` tag will be a standard. + +What I suggest is to **generate the responsive images markup** using some function written in a **server side language** (like PHP or similar), with a simple configuration to **switch** to make it generate the `span[data-picture]` element today (required by picturePolyfill) or the real `picture` element when fully supported. + + +### How the `img` is appended and updated + +Upon finding a matching media in the `data-picture` array, picturePolyfill will generate an `img` element and inside that span. +The `img`'s `src` attribute is updated at browser resize, after a small delay (100ms) to prevent the script to be executed too many times during smooth (animated or manually dragged) browser resize. + +## Server-side scaling/cropping tool + +Responsive images can be quite complicated to be served on your website if you have to: pre-scale them at many different resolutions; name them; and maybe change their size when developing a new release of your site. + +It's then a good practice to have a server-side picture scaling service (like [pixtulate](http://www.pixtulate.com/)) to scale the images for you, just in time, starting from only one big image. + +If you want to use an image server, you can code your HTML like the following: + +```html + + + + + +``` + +Use an array in the srcset property and double size images to support HD/retina displays, as you can see in the "With HD (Retina) images support" section of this readme. + +[Take a look at the demo](http://verlok.github.io/picturePolyfill/). + + +## Usage + +To use picturePolyfill, just insert the script tag at the end of your html file, just right the closure of the `body` tag. +If picturePolyfill is put in the head of the document of deferred until after load is fired, images will not load unless the browser window is resized. + +```html + + + Your HEAD content + + + Your BODY + your responsive images markup, as described + + + +``` + +### Later calls + +picturePolyfill is intentionally exposed to the global space, so you can call it later, as you need it. + +* **AJAX calls**: after your new DOM has been injected on the page, just call `window.picturePolyfill()` +* **document ready**: if you insert the ` + + + \ No newline at end of file diff --git a/usingPictureMarkup/picturePolyfill.js b/version1/picturePolyfill.js similarity index 56% rename from usingPictureMarkup/picturePolyfill.js rename to version1/picturePolyfill.js index 63aac43..1ad52e6 100644 --- a/usingPictureMarkup/picturePolyfill.js +++ b/version1/picturePolyfill.js @@ -9,45 +9,30 @@ mediaQueriesSupported = w.matchMedia && w.matchMedia("only all") !== null && w.matchMedia("only all").matches; - /** - * Returns a hash density > src - */ - - function getSrcSetHash(srcSetAttribute) { - var hash = {}, - sources = srcSetAttribute.split(','); - - for (var i=0, len=sources.length; i0) { + function getSrcFromSrcSet(arrayOrString, position) { + if (typeof arrayOrString === 'string') { + return arrayOrString; + } + while (arrayOrString[position]===undefined && position>0) { position-=1; } - return srcSetArray[position]; + return arrayOrString[position]; } /** * Loop through every element of the dataPicture array, check if the media query applies and, * if so, get the src element from the srcSet property based depending on pixel ratio - * @param dataPicture {element} + * @param dataPicture * @returns {string} */ @@ -57,7 +42,7 @@ for (var i=0, len=dataPicture.length; i0) ? + createOrUpdateImage(imageHolder, (mediaQueriesSupported) ? getSrcAttributeFromData(pictureData) : - pictureElement.getAttribute("data-defaultsrc")); - //} - //catch (e) { - //w.console.log(e); - //} + getStandardImageFromData(pictureData)); + } + catch (e) { + w.console.log(e); + } } } @@ -152,12 +131,12 @@ */ w.picturePolyfill = (!document.querySelectorAll) ? function(){} : function(element){ - parsePictures(element || document); + parseDOMTree(element || document); }; /** - * Manage resize event calling the parsePictures function + * Manage resize event calling the parseDOMTree function * only if they've passed 100 milliseconds between a resize event and another * to avoid the script to slower the browser on animated resize or browser edge dragging */ diff --git a/version1/picturePolyfill.min.js b/version1/picturePolyfill.min.js new file mode 100644 index 0000000..7cf5490 --- /dev/null +++ b/version1/picturePolyfill.min.js @@ -0,0 +1,4 @@ +/*! Picture Polyfill - v1.0.1 - 2014-03-15 +* https://github.com/verlok/picturePolyfill/ +* Copyright (c) 2014 Andrea Verlicchi; Licensed MIT */ +(function(a){function e(a,b){if(typeof a=="string")return a;while(a[b]===undefined&&b>0)b-=1;return a[b]}function f(b){var d,f;for(var g=0,h=b.length;gpicturePolyfill Test

Most complex usage

diff --git a/tests/js/picturefill.js b/version1/tests/js/picturefill.js similarity index 100% rename from tests/js/picturefill.js rename to version1/tests/js/picturefill.js diff --git a/tests/picturefill_vs_picturePolyfill.html b/version1/tests/picturefill_vs_picturePolyfill.html similarity index 100% rename from tests/picturefill_vs_picturePolyfill.html rename to version1/tests/picturefill_vs_picturePolyfill.html diff --git a/tests/tests.html b/version1/tests/tests.html similarity index 58% rename from tests/tests.html rename to version1/tests/tests.html index a746879..c1e0b95 100644 --- a/tests/tests.html +++ b/version1/tests/tests.html @@ -20,11 +20,11 @@

picturePolyfill Test

SrcSet not array

@@ -32,11 +32,11 @@

SrcSet not array

Bad formatted data-picture JSON

@@ -44,11 +44,12 @@

Bad formatted data-picture JSON

Missing standard image

@@ -56,11 +57,11 @@

Missing standard image

Good formatted data-picture JSON