{"database": "24ways", "table": "articles", "is_view": false, "human_description_en": "where author = \"Jake Archibald\", topic = \"ux\" and year = 2011", "rows": [[269, "Adaptive Images for Responsive Designs\u2026 Again", "When I was asked to write an article for 24 ways I jumped at the chance, as I\u2019d been wanting to write about some fun hacks for responsive images and related parsing behaviours. My heart sank a little when Matt Wilcox beat me to the subject, but it floated back up when I realized I disagreed with his method and still had something to write about.\n\nSo, Matt Wilcox, if that is your real name (and I\u2019m pretty sure it is), I disagree. I see your dirty server-based hack and raise you an even dirtier client-side hack. Evil laugh, etc., etc.\n\nYou guys can stomach yet another article about responsive design, right? Right?\n\nHalf the room gets up to leave\n\nWhoa, whoa\u2026 OK, I\u2019ll cut to the chase\u2026\n\nTL;DR\n\nIn a previous episode, we were introduced to Debbie and her responsive cat poetry page. Well, now she\u2019s added some reviews of cat videos and some images of cats. Check out her new page and have a play around with the browser window. At smaller widths, the images change and the design responds. The benefits of this method are:\n\n\n\tit\u2019s entirely client-side\n\timages are still shown to users without JavaScript\n\tyour media queries stay in your CSS file\n\tno repetition of image URLs\n\tno extra downloads per image\n\tit\u2019s fast enough to work on resize\n\tit\u2019s pure filth\n\n\nWhat\u2019s wrong with the server-side solution?\n\nResponsive design is a client-side issue; involving the server creates a boatload of problems.\n\n\n\tIt sets a cookie at the top of the page which is read in subsequent requests. However, the cookie is not guaranteed to be set in time for requests on the same page, so the server may see an old value or no value at all.\n\tServing images via server scripts is much slower than plain old static hosting.\n\tThe URL can only cache with vary: cookie, so the cache breaks when the cookie changes, even if the change is unrelated. Also, far-future caching is out for devices that can change width.\n\tIt depends on detecting screen width, which is rather messy on mobile devices.\n\tResponding to things other than screen width (such as DPI) means packing more information into the cookie, and a more complicated script at the top of each page.\n\n\nSo, why isn\u2019t this straightforward on the client?\n\nClient-side solutions to the problem involve JavaScript testing user agent properties (such as screen width), looping through some images and setting their URLs accordingly. However, by the time JavaScript has sprung into action, the original image source has already started downloading. If you change the source of an image via JavaScript, you\u2019re setting off yet another request.\n\nImages are downloaded as soon as their DOM node is created. They don\u2019t need to be visible, they don\u2019t need to be in the document.\n\nnew Image().src = url\n\nThe above will start an HTTP request for url. This is a handy trick for quick requests and preloading, but also shows the browser\u2019s eagerness to download images.\n\nHere\u2019s an example of that in action. Check out the network tab in Web Inspector (other non-WebKit development aids are available) to see the image requests.\n\nBecause of this, some client-side solutions look like this:\n\n<img src=\"t.gif\" data-src=\"real-image.jpg\" data-bigger-src=\"real-bigger-image.jpg\">\n\nwhere t.gif is a 1\u00d71px tiny transparent GIF.\n\nThis results in no images if JavaScript isn\u2019t available. Dealing with the absence of JavaScript is still important, even on mobile. I was recently asked to make a website work on an old Blackberry 9000. I was able to get most of the way there by preventing that OS parsing any JavaScript, and that was only possible because the site didn\u2019t depend on it.\n\nWe need to delay loading images for JavaScript users, but ensure they load for users without JavaScript. How can we conditionally parse markup depending on JavaScript support?\n\nOh yeah! <noscript>!\n\n<noscript>\n    <img src=\"image.jpg\">\n</noscript>\n\nWhoa! First spacer GIFs and now <noscript>? This really is the future! The image above will only load for users without JavaScript support. Now all we need to do is send JavaScript in there to get the textContent of the <noscript> element, then we can alter the image source before handing it to the DOM for parsing.\n\nHere\u2019s an example of that working \u2026 unless you\u2019re using Internet Explorer.\n\nInternet Explorer doesn\u2019t retain the content of <noscript> elements. As soon as it\u2019s parsed, it considers it an empty element. FANKS INTERNET EXPLORER. This is why some solutions do this:\n\n<noscript data-src=\"image.jpg\">\n    <img src=\"image.jpg\">\n</noscript>\n\nso JavaScript can still get at the URL via the data-src attribute. However, repeating stuff isn\u2019t great. Surely we can do better than that.\n\nA dirty, dirty hack\n\nThankfully, I managed to come up with a solution, and by me, I mean someone cleverer than me. Pornel\u2019s solution uses <noscript>, but surely we don\u2019t need that.\n\nNow, before we look at this, I can\u2019t stress how dirty it is. It\u2019s so dirty that if you\u2019ve seen it, schools will refuse to employ you.\n\n<script>document.write('<' + '!--')</script>\n<img src=\"image.jpg\">\n<!---->\n\nPhwoar! Dirty, isn\u2019t it? I\u2019ll stop for a moment, so you can go have a wash.\n\nDone? Excellent.\n\nWith this, the image is wrapped in a comment only for users with JavaScript. Without JavaScript, we get the image. Unlike the <noscript> example above, we can get the text content of the comment pretty easily.\n\nHurrah! But wait\u2026 Some browsers are sometimes downloading the image, even with JavaScript enabled. Notably Firefox. Huh?\n\nImages are downloaded in comments now? What?\n\nNo. What we\u2019re seeing here is the effect of speculative parsing. Here\u2019s what\u2019s happening:\n\n\n\nWhile the browser is parsing the script, it parses the rest of the document. This is usually a good thing, as it can download subsequent images and scripts without waiting for the script to complete. The problem here is we create an unbalanced tree.\n\n An unbalanced tree, yesterday.\n\nIn this case, the browser must throw away its speculative parsing and reparse from the end of the <script> element, taking our document.write into consideration. Unfortunately, by this stage it may have already discovered the image and sent an HTTP request for it.\n\nA dirty, dirty hack\u2026 that works\n\nPornel was right: we still need the <noscript> element to cater for browsers with speculative parsing.\n\n<script>document.write('<' + '!--')</script><noscript>\n    <img src=\"image.jpg\">\n</noscript -->\n\nAnd there we have it. We can now prevent images loading for users with JavaScript, but we can still get at the markup.\n\nWe\u2019re still creating an unbalanced tree and there\u2019s a performance impact in that. However, the parser won\u2019t have got far by the time our script executes, so the impact is small. Unbalanced trees are more of a concern for external scripts; a lot of parsing can happen by the time the script has downloaded and parsed.\n\nUsing dirtiness to create responsive images\n\nNow all we need to do is give each of our dirty scripts a class name, then JavaScript can pick them up, grab the markup from the comment and decide what to do with the images.\n\nThis technique isn\u2019t exclusively useful for responsive images. It could also be used to delay images loading until they\u2019ve scrolled into view. But to do that you\u2019ll need a bulletproof way of detecting when elements are in view. This involves getting the height of the viewport, which is extremely unreliable on mobile devices.\n\nHere\u2019s a hastily thrown together example showing how it can be used for responsive images.\n\nI adjust the end of the image URLs conditionally depending on the result of media queries. This is done on page load, and on resize.\n\nI\u2019m using regular expressions to alter the URLs. Using regex to deal with HTML is usually a sign of insanity, but parsing it with the browser\u2019s DOM parser would trigger the download of images before we change the URLs. My implementation currently requires double-quoted image URLs, because I\u2019m lazy. Wanna fight about it?\n\nMedia querying via JavaScript\n\nJeremy Keith used document.documentElement.clientWidth in his example, which is great as a proof of concept, but unfortunately is rather unreliable across mobile devices.\n\nThankfully, standards are coming to the rescue with window.matchMedia, which lets us provide a media query string and get a boolean result. There\u2019s even a great polyfill for browsers that don\u2019t support it (as long as they support media queries in CSS).\n\nI didn\u2019t go with that for three reasons:\n\n\n\tI\u2019d like to keep media queries in the CSS file, if possible.\n\tI wanted something a little lighter to keep things speedy while resizing.\n\tIt\u2019s just not dirty enough yet.\n\n\nTo make things ultra-dirty, I add a test element to the page with a specific class, let\u2019s say media-test. Then, I control the width of it using media queries in my CSS file:\n\n@media all and (min-width: 640px) {\n    .media-test {\n        width: 1px;\n    }\n}\n@media all and (min-width: 926px) {\n    .media-test {\n        width: 2px;\n    }\n}\n\nThe JavaScript part changes the URL suffix depending on the width of media-test. I\u2019m using a min-width media query, but you can use others such as pixel-ratio to detect high DPI displays. Basically, it\u2019s a hacky way for CSS to set a value that can be picked up by JavaScript. It means the bit that signals changes to the images sits with the rest of the responsive code, without duplication.\n\nAlso, phwoar, dirty!\n\nThe API\n\nI threw a script together to demonstrate the technique. I\u2019m not particularly attached to it, I\u2019m not even sure I like it, but here\u2019s the API:\n\nresponsiveGallery({\n    // Class name of dirty script element(s) to target\n    scriptClass: 'dirty-gallery-script',\n    // Class name for our test element\n    testClass: 'dirty-gallery-test',\n    // The initial suffix of URLs, the bit that changes.\n    initialSuffix: '-mobile.jpg',\n    // A map of suffixes, for each width of 'dirty-gallery-test'\n    suffixes: {\n        '1': '-desktop.jpg',\n        '2': '-large-desktop.jpg',\n        '3': '-mobile-retina.jpg'\n    }\n});\n\nThe API can cover individual images or multiple galleries at once. In the example I gave at the start of the article I make two calls to the API, one for both galleries, and one for the single image above the video reviews. They\u2019re separate calls because they respond slightly differently.\n\nThe future\n\nHopefully, we\u2019ll get a proper solution to this soon. My favourite suggestion is the <picture> element that Bruce Lawson covers.\n\n<picture alt=\"Angry pirate\">\n    <source src=\"hires.png\" media=\"min-width:800px\">\n    <source src=\"midres.png\" media=\"min-width:480px\">\n    <source src=\"lores.png\">\n    <!-- fallback for browsers without support -->\n    <img src=\"midres.png\" alt=\"Angry pirate\">\n</picture>\n\nUnfortunately, we\u2019re nowhere near that yet, and I\u2019d still rather have my media queries stay in CSS. Perhaps the source elements could be skipped if they\u2019re display:none; then they could have class names and be controlled via CSS. Sigh.\n\nWell, I\u2019m tired of writing now and I\u2019m sure you\u2019re tired of reading. I realize what I\u2019ve presented is a yet another dirty hack to the responsive image problem (perhaps the dirtiest?) and may be completely unfeasible in professional situations. But isn\u2019t that the true spirit of Christmas?\n\nNo.", "2011", "Jake Archibald", "jakearchibald", "2011-12-08T00:00:00+00:00", "https://24ways.org/2011/adaptive-images-for-responsive-designs-again/", "ux"]], "truncated": false, "table_rows_count": 336, "filtered_table_rows_count": 1, "expanded_columns": [], "expandable_columns": [], "columns": ["rowid", "title", "contents", "year", "author", "author_slug", "published", "url", "topic"], "primary_keys": [], "units": {}, "query": {"sql": "select rowid, * from articles where \"author\" = :p0 and \"topic\" = :p1 and \"year\" = :p2 order by rowid limit 101", "params": {"p0": "Jake Archibald", "p1": "ux", "p2": "2011"}}, "facet_results": {}, "suggested_facets": [], "next": null, "next_url": null, "query_ms": 14.222860336303711}