6

We're currently building a website for mobile devices. Supported operating systems and browsers should be:

  • Android 4.x (Stock Browser, Google Chrome)
  • iOS6+ (Safari, Google Chrome)

In order to also support high resolution displays we evaluated various techniques and libraries to automatically replace images with their high-res pendants:

Attempt 1: retina.js http://retinajs.com/

The first attempt was to use a normal <img> Tag like this: <img src="foo.png"> and use retina.js to let it automatically replace the src Attribute with the name of the retina image (foo@2x.png). This works but has 2 downsides: First, it will create unwanted overhead because both, the original as well as the retina image, would be loaded and second, if there is no retina image available it will cause lots of 404 errors on server log which we do not want.

Attempt 2: picturefill.js https://github.com/scottjehl/picturefill

This framework uses some weird html markup based on <span> elements. For me it looks like as if the author tried to mimic the proposed <picture> element which is not a standard (yet), see http://picture.responsiveimages.org - I don't like this approach because of the weird markup. For me it doesn't make sense to semantically describe images with spans.

Attempt 3: Replace images via CSS property background-image

I see sometimes people using CSS media queries to detect retina displays and then set a background-image on a div (or similar element) with a higher or lower solution picture. I personally don't like this approach either because it completely discourages creating semantically good markup à la <img src="foo.png">. I can't imagine building a website just with div's and then set all images as background images - This just feels very weird.

Attempt 4: Set images via CSS property content:url(...)

As proposed here Is it possible to set the equivalent of a src attribute of an img tag in CSS? it seems to be possible to overwrite the src Attribute in img Tags via CSS using the property content:url(). Now, here is the plan: We set img tags for each image with a transparent blank 1x1 png referenced in its src attribute, like this: <img id="img56456" src="transp_1x1.png" alt="img description">. Now this is semantically ok and also valid against the W3C validator. Then we load a CSS Stylesheet that sets all the images on the website via Media Queries.

Example:

#img56456{content:url(foo.png)}
@media (-webkit-min-device-pixel-ratio: 2){ 
    #img56456{content:url(foo@2x.png)}
}

Now, this approach works pretty good:

  • No overhead
  • Solid markup
  • Works on the required devices/browsers
  • SEO for Images is not requirement here

Now, could this approach cause any side effects we didn't think of? I am just asking because I know it works but kinda "feels" weird to set all images via CSS and I also found this comment on this approach on SO Is it possible to set the equivalent of a src attribute of an img tag in CSS?:

"Worth to add that even in browsers that support assigning content to img, it changes its behavior. The image starts to ignore size attributes, and in Chrome/Safari it loses the context menu options like 'Save image'. This is because assigning a content effectively converts img from empty replaced element to something like <span><img></span>"

Could this be a problem? (I didn't notice any sizing problems and the context menu is not a requirement)

Community
  • 1
  • 1
Timo Ernst
  • 15,243
  • 23
  • 104
  • 165
  • Two other options include [always serving retina then letting the browser downgrade](http://net.tutsplus.com/tutorials/html-css-techniques/the-right-way-to-retinafy-your-websites/) and using [sprites with position shifting media queries](http://miekd.com/articles/using-css-sprites-to-optimize-your-website-for-retina-displays/). These are my preferences for large and small images, respectively. – bishop Jan 29 '14 at 14:36
  • @bishop I thought about always serving the retina image too but the overhead for non-retina devices is to high imho, especially on mobile. – Timo Ernst Jan 29 '14 at 14:53
  • Yeah, but you get caching, which to me is a bigger deal. Besides, if you [optimize your 2x images](http://imageoptim.com/ipad.html), the penalty is not that substantial. I would also add that this has to be handled case-by-case: SVG, font files, CSS shapes, and sprites should all be preferred before serve-retina-and-downsample. – bishop Jan 29 '14 at 15:42
  • @bishop From what I can observe, caching works as expected.. – Timo Ernst Jan 29 '14 at 16:16
  • I generally haven't found the overhead of downgrading a retina-first image to be prohibitive, imo. Saving for retina generally allows you to save jpgs at 0-10% quality and still look good on both retina and non-retina devices. The size at 0-10% quality is comparable to 70-80% quality on an image half it's size. Saying that, if you can't do this (PNGs for example) and SVGs aren't appropriate, then the only real issue here is that you're downloading both images for retina devices, which is in itself an overhead issue. – niaccurshi Jan 30 '14 at 09:32
  • @niaccurshi What I was trying to say is: If you always serve your retina image to all devices (even to those that do not have a retina display) you'll get an overhead on non-retina devices because you serve 50% more data than their display is capable to show. – Timo Ernst Jan 31 '14 at 09:46
  • I understand that @Timo, but I don't believe there is an overhead issue if you serve one single retina optimised image to all devices. Having used this technique before, I've found that a well optimised retina dimension image is only marginally (not 50%) more data heavy than a similarly well optimised non-retina dimension image. Obviously this needs to be caveated with the fact it depends on the image and the file format you're using! – niaccurshi Feb 02 '14 at 12:46
  • @niaccurshi I see your point! However, for example when I surf the web while sitting on the toilet (where signal quality is really really bad) I often appreciate it when a website serves images with as few kb as possible. I call this the "toilet first" initiative :-) - I personally believe that network data for mobile should be kept to an absolute minimum because every byte counts if signal quality is bad. – Timo Ernst Feb 03 '14 at 10:25
  • @Timo, I agree. But older or less standards compliant phone browsers would download BOTH your images, not just one. My point is that correctly optimising your retina jpg image will make your image, especially if it is a more complicated jpg, the *same file size* as the non-retina one. You're not saving anything, and you're having to maintain two files. There isn't a one size fits all solution, but sometimes you just don't *need* two files to save filesize :) If I get time I'll stick an example up showing this soon. – niaccurshi Feb 04 '14 at 17:39
  • @niaccurshi Why would the browser download both files? That's exactly what I am trying to avoid :-) – Timo Ernst Feb 06 '14 at 10:04
  • I think this might answer some questions: http://timkadlec.com/2012/04/media-query-asset-downloading-results/ – niaccurshi Feb 09 '14 at 16:05
  • @niaccurshi Very cool link! Especially test number 3 where the image is not being loaded when the parent element is set to display:none looks interesting. – Timo Ernst Feb 11 '14 at 10:10
  • 2
    I didn't understand, why you think that picturefill has weird markup. We use it in our project and it does very well its job without any drawback. If you want to use it only for retina images you don't actually need any span or picture element. All you need is an img tag with a srcset attribute. e.g: ``. Remind that Chrome has now native support for picture element and srcset attribute. – Deniz Oct 29 '14 at 17:08
  • @DenizToprak Yeah, the new picturefill is awesome and I can fully recommend it. The old version relied on a weird markup structure based on 's. – Timo Ernst Oct 30 '14 at 09:16

2 Answers2

0

There are lots of advantages and disadvantages, but one disadvantage is that the image won't be cached. This is more of a problem on mobile devices where internet is generally slower and more expensive for the user (if on a data connection not wifi).

I don't know about those libraries but you could use media-queries, depending on how many images you have, otherwise it would be lots of code variations to write. And have a threshold screen size at which point you use a different file. Only one file is actually loaded, even though you specify both in the media-queries.

S..
  • 5,511
  • 2
  • 36
  • 43
  • Wow, thanks for the hint about caching. If that's true the technique is definitely a no-go :-( – Timo Ernst Jan 29 '14 at 14:52
  • I just tested this on Google Chrome (Desktop version) and from what I can observe, an image which is provided via css `content:url()` is properly cached when I refresh the page. At least in Chrome. – Timo Ernst Jan 29 '14 at 16:10
  • Just also tested on an iPhone (iOS7) and caching seems to work properly. – Timo Ernst Jan 29 '14 at 16:15
  • Can you provide a reference for this claim? There's no reason applying the resource using different DOM methods should have any affect on caching — that's something between the requested resource and the browser. – Barney Jan 30 '14 at 09:28
  • 1
    I think it is better that the server delivers the matching images. – Mirko Brunner Feb 02 '14 at 16:08
  • @MirkoBrunner Good point! That sounds like a good technique but requires some extra work tho since the server-side part would need some extra logic that parses the client's user agent and then checks it against a device-database to decide wether it is a retina device or not. – Timo Ernst Feb 03 '14 at 10:42
  • @Timo Yes it is a "little bit" more work. But the best solution for the customers / users. I have quickly found that PHP Class on GitHub (https://github.com/serbanghita/Mobile-Detect). – Mirko Brunner Feb 03 '14 at 11:04
0

The new version of picturefill http://scottjehl.github.io/picturefill/ doesn't rely on <span> any more. Instead it simply uses the official HTML5 srcset attribute and mimics its behavior if the browser doesn't support it, so for me this is the ideal solution after quite a while now.

Using the CSS property content:url(...) was a neat little idea but it made things a little complicated and hackish too.

So, to answer my own question: No, it's a bad idea. Using the new version of picturefill is a way better solution. You can even remove it after a while when newer versions of all major browsers support the srcset attribute and you'll still be standard compliant. http://caniuse.com/#search=srcset

Example:

<img srcset="examples/images/small.jpg, examples/images/medium.jpg 2x" alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia">
<script src="picturefill.js"></script>
Timo Ernst
  • 15,243
  • 23
  • 104
  • 165