Altering image sizes responsively in all browsers

If you view the responsive web page page by Ethan Marcott in IE8, you’ll notice that it doesn’t work in the same way as modern browsers. This is because IE8 and below doesn’t support media queries – the smart bit of css we use to conditionally style our page based on current or device width. We also need to evaluate media queries to set our img tag through javascript using windows.matchMedia and media query list addListener– this isn’t supported in IE9 or less.

We need another polyfill – to add media queries to IE8 and below and to add media query matching and listening in IE9 (as well as any other unsupported platforms). The responsive polyfill I’ve written combines elements of Scott Jehl’s respond.js polyfill for media queries, Paul Irish’s matchMedia.js polyfill and my own code for media query listeners whilst keeping performance a priority.

This allows us to use code to detect when to change the image or image size in the browser and set the correct image path on initial page load. We thus take the following steps:

– set up a media query to detect changes at a width of 600 pixels
– read the contents of noscript tags and add an image to the dom depending on the media query
– listen to the media query so that when it changes we can modify the image path accordingly.

var mql = window.matchMedia('only screen and (max-width: 600px)');
var elements;
	
//Get contents of noscript tags 
var tags = document.getElementsByTagName("noscript");

//Add image to dom depending on media query
for (var i=0, len=tags.length; i<len; i++) {
  var markup = tags[i].textContent.trim();

  if (mql.matches) markup = markup.replace("/large/", "/small/");
  tags[i].insertAdjacentHTML('afterend', markup);
}

//Scale up to large / down to small when media query changes
mql.addListener(function() {

  if (!elements) elements = document.getElementsByTagName("img");
		
  var from = (mql.matches) ? "/large/" : "/small/";
  var to = (mql.matches) ? "/small/" : "/large/";

  for (var i=0, len=elements.length; i<len; i++) {
    if (elements[i].className.indexOf("responsive") > -1)
      elements[i].src = elements[i].src.replace(from, to);
  }
});

View the example

Thoughts on this technique

It should be pointed out that strictly speaking, once we have the large image we dont need the small image - it is better just to scale it down. However I also think that the smaller image in a responsive design may often be different - perhaps a more cropped version - anyway this page exists to demonstrate the basic technique only.

We also dont use a jQuery style document.ready type approach - this is simply not needed for such a simple example - placing the code in the footer is more than sufficient. However - because the images are loaded fairly late in the day you can see the image 'waterfall' as they are loaded. A more advanced technique may only display them once they are fully loaded, perhaps with css such as

img.responsive {display:none}
noscript img.responsive {display:block}

Is it worth the extra script vs the reduced download? Probably. Certainly the noscript code is small. Making IE9 and below fully responsive may not be worth it however.

PS. The responsive script doesn't currently read style tags in the parent document - it could easily be modified to do so however.

Altering image sizes responsively in all browsers

Progressive enhancement of NOSCRIPT tags containing images

For the 2nd of our 4 part plan to solve the problem of responsive images using current browser standards, we need to put our noscript textContent polyfill to work. What better than to refactor the seminal responsive design original from A List Apart by Ethan Marcotte.

To start with, all we do is move the 6 character images into noscript tags, add our polyfill ot the bottom of the body and run a small amount of cross browser script to append the content of the noscript tag after itself. You can see the results here.


<script type="text/javascript" src="js/noscript.min.js"></script>
<script type="text/javascript">
var tags = document.getElementsByTagName("noscript");
for (var i=0, len=tags.length; i<len; i++) {
  var tag = tags[i];
  tag.insertAdjacentHTML('afterend', tag.textContent);
}
</script>


 
This provides us with the hook we need to dynamically modify image paths before they are added to the DOM.

Progressive enhancement of NOSCRIPT tags containing images

Read noscript tag content reliably in all browsers

The first step in our 4 part plan to solve the problem of responsive images using current browser standards is to reliably access the content inside a noscript tag. Modern browsers support access to the content of the noscript tag through either the innerHTML property or the textContent property. Since innerHTML is encoded, and is also read-only in IE<9, and textContent seems to contain markup perfectly in supported browser, it is the property we will polyfill.

The first job is to feature detect the property, which is done simply with this piece of code:

var div = document.createElement("div"); 
div.innerHTML = "<noscript><span></span></noscript>"; 
var elements = div.getElementsByTagName("noscript"); 
if (elements.length && elements[0].textContent) return; 

 
If we need to add the property, we have to resort to retreiving the markup of the page and parsing it with a regex. I know this may be frowned upon, but in this case it is quick and fairly reliable. In this case we also need to path the XmlHttpRequest object, and then retrieve the markup of the current browser location via ajax.

patchHttpRequest(); 
var xmlhttp = new XMLHttpRequest(); 
xmlhttp.onreadystatechange = function() { 
  if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { 
    var regex = /<script (.|n)*?>(.|n)*?</script>/igm; 
    var tags = document.getElementsByTagName("noscript"); 
..

 
Finally, we loop through each noscript tag, and set the textContent property.

var results = [], response = xmlhttp.responseText; 
var i=0, len=tags.length;
while (i < len && results) {
  results = regex.exec(response); 
  tags[i].textContent = results[1]; 
  i++; 
} 

 
Visit the github page to view the code packed together with some dom and ajax helper code.

Read noscript tag content reliably in all browsers

Responsive Images – what’s the problem?

Something I’ve been grappling with for a while is a good solid approach to loading the correct image at the correct screen size in a responsive design. This is hard one lots of people have been working on. Mainly because using an image tag means unrequired images are loaded before you have a change to change the src attribute, and there isn’t a semantic alternative available to the img tag (yet).

So what we need is a clean easy to use approach, that is semantic and can be progressively enhanced, is responsive, and that works across all browsers on all types of devices. Here’s the approach I will be following in the next few posts:

1. Consistently detect the contents of noscript tag
2. Append an appropriate img tag to the markup for script-enabled browsers
3. Change the image based on a call to matchMedia and the resulting MediaQueryList
4. Bonus: add support for Google’s webP format

Responsive Images – what’s the problem?

Introducing jQuery superpng plug-in

The superpng plug-in has been written to provide a flexible png alpha api to developers using jQuery and who need to support IE6. Other scripts didn’t provide the performance, features or flexibility that I required, as well as not using functionality available in jQuery which is fairy ubiquitous these days. The script uses either the AlphaImageLoader filter or the vml approach, depending on the type of tag and whether background-position is required.

For more information on alpha pngs and IE6, please see my previous post here.

Usage

Enable alpha images for all elements with a class of .png providing the relative path of the replacement transparent gif image:

$(document).ready(function() {
	$('.png').superpng({path: '../images/blank.gif'});
});

The full list of options are as follows:

  • path: define the location of the transparent .gif file used to replace the original png image
  • mode: filter/vml/auto (default) – describes the method used to enable png alpha.
    • filter:  The alpha loader fix is used, replacing the existing image with the blank image found in path.
    • vml: vml markup is placed inside the container. Requires a <div> or other block element.
    • auto:  A mode is chosen based on the tag type and background-position value.
  • cache: Determines if background images are cached. Default true.

Superpng is licenced under the the MIT, BSD, and GPL Licenses. Online examples are available here and the code is available on Github.

Please contact me with any feedback or any issues you may encounter.

Introducing jQuery superpng plug-in

Transparent .png images in IE6

A lot of web design I’m involved with these days involves either animating images over backgrounds, or creating composite layouts made up of multiple layered images. These techniques require placing images over one another, and blending the images (usually with shadows) together. This approach requires images with something called alpha (partial) transparency. The alpha channel is a separate stream of data that specifies the level of transparency for each pixel in the image. In web browsers this functionality is available as a 24 bit .png, with 8 bits each for alpha, red, green and blue.

Now for the kicker. Alpha transparency isn’t available in IE6, and unfortunately as I write this, corporate customers still dictate that we support Microsoft’s finest browser creation.

As has been documented for some time, certain workarounds exist for displaying transparent images in IE6. Firstly, the original sleight method used the proprietary alpha image loader filter, and then variations to allow for background images and css – such as supersleight and Angus Turnbull’s png fix – appeared as web 2.0 took off and we started to make more handsome and detailed web pages.

A recent discussion with a work collegue highlighted one major flaw with this approach – the alpha image loader does not support background-position – and as every decent front-end developer knows, you can’t sprite images without this bit of handy css. I’ve also recently discovered that according to Stoyan Stefanov at Yahoo!, the alpha image loader is in fact also very bad for performance.

With this issue in mind, I came across a different technique for loading alpha png images in IE6. Drew Diller’s technique loads a VML element into the DOM which can then be styled using a full alpha .png. It also appears to be faster than the alpha image loader – though if the browser doesn’t have access to javascript then the alpha image loader would appear to be the only option.

With IE6 usage down to as little as < 5%, perhaps we don't have to wait much longer to leave these dirty little hacks behind us.

Transparent .png images in IE6