Respon­sive image

Il y a eu des centaines d’ar­ticles tech­niques détaillés et plus ou moins smart sur la possi­bi­lité de télé­char­ger une image plus ou moins grosse suivant la taille d’af­fi­chage, afin de ne pas utili­ser une énorme image sur mobile ou une ridi­cu­le­ment petite sur un écran 24″.

Si vous ne devez en lire qu’un

Le dernier pour comprendre où en sont les réflexions, c’est proba­ble­ment l’ar­ticle de Bruce Lawson. Il faut aussi lire les commen­taires.

Tout d’abord oubliez les astuces à base de javas­cript et de noscript. Il existe des machins horribles qui résistent à peu près à tout, mais ça reste fran­che­ment bancal. Oubliez encore plus les scripts à base de cookie, qui de toutes façons ne pour­ront jamais répondre à plus du tiers de la problé­ma­tique, et encore, avec des effets de bords.

Bruce part d’une solu­tion unique­ment basée sur des CSS, qui de plus à la bonne idée d’être théo­rique­ment déjà fonc­tion­nelle. Il suffi­rait d’amé­lio­rer le support CSS 3 des navi­ga­teurs pour que cela ne se pose plus. Pour l’ins­tant cela n’est possible qu’a­vec Opera et Chrome, et les opti­mi­sa­tions de perfor­mance de ces navi­ga­teurs risquent de faire télé­char­ger deux images au lieu d’une seule (ce qui est un peu l’op­posé du but recher­ché).

Il propose ensuite un marquage HTML pour arri­ver au même résul­tat. C’est rétro-compa­tible avec les navi­ga­teurs actuels, et ne devrait pas être impos­sible à implé­men­ter.

Main­te­nant ça ne me plait pas

Tout d’abord le marquage HTML me semble le mauvais endroit pour résoudre la problé­ma­tique. On parle de répondre à des tailles d’af­fi­chage, et ça c’est typique­ment une ques­tion de présen­ta­tion, donc de CSS. Certai­ne­ment il y a des fois où un marquage HTML aura du sens, mais selon moi ce sera un cas parti­cu­lier du cas géné­ral, et utili­ser HTML est prendre le problème par le mauvais sens.

Ensuite il y a des problé­ma­tiques qui marquent un manque de recul (pas de la personne, mais bien en rapport avec les besoins réels et les capa­ci­tés des navi­ga­teurs). Filtrer sur le fait que l’uti­li­sa­teur est en 3G est seule­ment impos­sible pour beau­coup de situa­tions (le navi­ga­teur n’a pas l’in­for­ma­tion), mais aussi n’a aucun sens. Pour une même connexion 3G je peux être à des vitesses réelles qui font presque passer mon ancien 56K pour une alter­na­tive accep­table (par exemple à cause des pertes de paquets à gogo), soit être à 7Mb/s et crâner devant la majo­rité des liai­sons ADSL de mon pays (qui est pour­tant très bien connecté). De toutes façons la vitesse de connexion sur mon propre accès est un très mauvais révé­la­teur de la vitesse réel­le­ment dispo­nible pour joindre le serveur d’en face. Le réseau peut être encom­bré chez moi, chez mon FAI, sur le serveur d’en face, ou n’im­porte où au milieu.

Je seconde le commen­taire numéro 8 : s’il fallait vrai­ment tailler le contenu de manière fixe en fonc­tion unique­ment de taille d’écran et de vitesse de connexion, une décla­ra­tion dans les entêtes de la requête et une négo­cia­tion HTTP seraient bien plus effi­caces. L’op­tion a de plus l’avan­tage de ne poser aucun problème de compa­ti­bi­lité arrière.

La problé­ma­tique de base

Toute­fois, on revient au problème initial. À force de discu­ter certains ont oublié la problé­ma­tique de base : choi­sir une image en fonc­tion de la taille à affi­cher. Le méca­nisme éven­tuel ne doit prévoir que ça : permettre de spéci­fier diffé­rentes adresses (ou diffé­rents suffixes) en fonc­tion de diffé­rentes hauteurs ou largeurs.

Charge à vous d’uti­li­ser une entête ou l’adresse IP côté serveur pour véri­fier si c’est de l’ADSL ou de la 3G (ça me semble une mauvaise idée mais vous pouvez déjà le faire). Charge à vous d’uti­li­ser des @media pour propo­ser plusieurs versions en fonc­tion de la taille de l’écran ou de son orien­ta­tion, ou de contraindre cette taille en fonc­tion. En combi­nant tout cela vous devriez pouvoir faire tout ce qui vous amuse, mais la problé­ma­tique qui nous manque c’est unique­ment celle de four­nir plusieurs URLs en fonc­tion de la taille prévue pour l’af­fi­chage. De toutes façons on ne trou­vera jamais de solu­tion qui fait le café.

Je n’ai pas « la » solu­tion, mais selon moi (et je rejoins beau­coup le commen­taire 15) :

  • La réponse prin­ci­pale doit être côté CSS (quitte à avoir d’autres types de réponses ailleurs pour des cas niches)
  • Elle ne doit s’oc­cu­per que de propo­ser des images alter­na­tives en fonc­tion de la largeur ou hauteur prévue pour affi­cher l’image (et non de la taille du view­port ou d’autres para­mètres tiers)
  • Elle ne doit pas provoquer de double télé­char­ge­ment sur les navi­ga­teurs non compa­tibles
  • Elle doit avoir un fall­back accep­table sur les navi­ga­teurs non compa­tibles

Le reste se fait avec les outils exis­tants, pas en les remplaçant.

Le jeu de « ma solu­tion à moi »

Si vrai­ment je devais créer quelque chose à chaud (ce qui se révè­lera forcé­ment une erreur), j’au­rai quelque chose comme ça :

img.intro {
  content: content-if(attr(data-big), width > 300px),
           content-if(attr(data-small), height < 50),
           attr(src) ;
}
@media all and (max-width:600px) {
  img.intro {
    width: 300px ;
    height: 200px ;
  }
}
@media all and (max-width:320px) {
  img#thingy {
    width: 50px ;
    height : 30px ;
  }
}

Bon, le pseudo langage sur la condi­tion n’est proba­ble­ment pas celui qu’il faut rete­nir mais il est volon­tai­re­ment basé sur un jeu de mot clefs limité et des contraintes qui le sont tout autant (hauteur et largeur dispo­nibles, c’est tout). On peut tout à fait envi­sa­ger que cela ne fonc­tionne que si les tailles width ou height sont déter­mi­nées expli­ci­te­ment dans la CSS, histoire de ne pas rendre l’im­plé­men­ta­tion irréa­li­sable. Autre avan­tage : c’est à priori compa­tible avec l’exis­tant puisqu’au pire si content-if n’est pas supporté, c’est toute la règle qui est igno­rée. Reste au navi­ga­teur qui supporte ça de prendre la première version qui corres­pond.

15 commentaires

  1. Puisque c’est moi qui t’ai « forcé »[1] à écrire ce billet, je me permets de répondre… ;-)

    Premier point : Bien qu’ayant invité tes lecteurs à lire les commentaires en plus du billet, tu reproches que l’on se base sur un type de réseau (« 3g » par exemple) qui ne signifie pas grand chose niveau bande passante. C’était bien l’un des objets de mon premier commentaire [2]. Finalement, tu évacues presque complètement ce postula de base qui m’intéresse le plus particulièrement, c’est à dire l’adaptation du contenu et/ou de sa présentation en fonction de la performance, et non (ou en plus) de l’espace visuel disponible.

    Second point : tu loues la solution CSS de Bruce, mais tu indiques toi-même qu’elle n’est que « théoriquement » fonctionnelle. On parle beaucoup en ce moment de choses pas très reluisantes au niveau de la « pureté » du code ou de la séparation parfaite entre contenu, présentation et comportement, justement parce que la théorie ne suffit pas, il faut des choses qui fonctionnent là, maintenant. La solution de Jake Archibald[2] est clairement un gros hack, voire même l’assemblage de plusieurs hacks, mais elle a le mérite de fournir une réponse pragmatique à un besoin réel. Les autres solutions vues jusqu’à présent me semble moins intéressantes, même si moins basées sur des hacks.

    Troisième point : tu dis que la solution HTML de Bruce ne te convient pas, parce qu’il s’agit d’adapter une présentation et non un contenu, mais je ne suis pas d’accord. Si on a moins d’espace ou de temps pour s’exprimer, il est légitime de vouloir s’exprimer différemment, d’adapter son « discours », sans uniquement changer son enrobage visuel. Je préfère envoyer une image avec moins d’information si j’ai moins d’espace ou si le débit me pousse à aller à l’essentiel. Envoyer une version identique mais de taille réduite, ce que proposent quasiment toutes les solutions d’images « responsives », n’est pas toujours pertinent.

    Quatrième point : je ne comprends pas ta solution, il faudrait que tu l’explicites un peu… ;-)

    [1] http://twitter.com/edasfr/statuses/144811013659832320
    [2] http://www.brucelawson.co.uk/2011/notes-on-adaptive-images-yet-again/#comment-851194
    [3] http://24ways.org/2011/adaptive-images-for-responsive-designs-again

    1. Pour ton premier point, j’en parle aussi dans le même paragraphe, mais je ne suis peut être pas clair. Aussi pour être complet : Je ne crois pas qu’il soit possible de catégoriser la performance sur un mode déclaratif, même si on limitait la notion de performance à la notion de débit. Tu connais assez les difficultés de mesures statistiques de bande passante réelle. Je ne vois aucun moyen sérieux, scalable et fiable pour qualifier la bande passante réelle de quelqu’un qui soit utilisable pour ce type de fonctionnalités.

      Si jamais il devait y avoir un tel moyen, alors selon moi ça doit être plus présent dans l’auto-négociation HTTP que dans la déclaration HTML. Je vais chercher une image, la négociation HTTP me permet de spécifier ma langue, des formats, et pourquoi pas à l’avenir des paramètres de poids/qualité, et j’ai ma ressource dans une forme adapté. Tout ça n’est pas lié à la balise et à HTML.

      Pour le second point, oui, on parle là de futures solution. Je ne vois aucune bidouille sérieusement valable, même à base de javascript, mais je comprend leur rôle. Par contre ici Bruce imagine aussi une solution qui n’existe pas encore quand il fait sa solution pure HTML. Quitte à concevoir une nouvelle solution, je préfère la construire sur de bonnes bases, c’est uniquement de ça que je discute.

      Notes tout de même que si la solution CSS de Bruce est encore peu utilisable, sa solution HTML ne l’est elle pas du tout. Bref, ça n’entre pas en ligne de compte dans ma réflexion actuelle.

      Pour le troisième point on a un peu échangé sur twitter. Pour moi la grande majorité des cas c’est réellement une question d’adaptation de taille. Il n’est pas dit que l’adaptation de taille se contente de redimensionner. On peut couper des choses, de même manière que parfois on cache des menus. Il n’en reste pas moins que la source est clairement dans une problématique de présentation.

      Peut être qu’avec un autre design les choix seront différents. Par contre si on changeait d’image pour une de même format, probablement que les choix et critères seraient les mêmes. C’est ce qui me fait dire que c’est plus de la présentation.

      Je t’accorde qu’il peut y avoir un besoin pour des images qui s’adaptent avec de réelles différences de contenu, par exemple pour des diagrames, où on choisit d’avoir plus ou moins de détail. Je persiste à penser que ça reste minoritaire dans le problème, mais ça reste couvert par une solution où les URL des différentes versions sont dans le HTML mais où la logique d’adaptation et les choix de taille sont dans la CSS.

  2. Welcome to the responsive images festivities :)
    I have a slight issue with your solution (and all CSS based solutions) – In case that the CSS is external (which is the usual case), the speculative parser will not be able to start fetching images until the CSS is loaded, or if it did, it would have to terminate them in-flight or reissue them once it gets the « real » URL. That would practically render the speculative parser + preloader ineffective.
    An HTML based solution would not have this issues. I once suggested ( http://blog.yoav.ws/2011/07/Simpler-responsive-images-proposal ) adding a media attribute to the base tag to basically add prefixes that would indicate the required image dimensions according to media queries.

    1. Hi

      Yep, I’ve seen that, and tried to write about it in my comment just when you were writing yours. Problem is that I doubt we will ever fix the speculative parser if not with dirty hacks.

      For adaptative images to work, the browser needs data about size, layout, and such things are per definition not available in the speculative parser (CSS is not applied, DOM is not even built).

      Either we try to hide our images from the parser with dirty hacks, which will always be a hack, and may break at any moment when they enhance the parser, or we use pure javascript with no or bad fallback. Best hack I’ve seen in this way is http://24ways.org/2011/adaptive-images-for-responsive-designs-again but it’s still really dirty.

      IMHO, the speculative parser problem has to be fixed by a specific attribute or a specific meta tag, either at the global level (to deactivate the speculative download) or at the image level (to opt out for a specific image). Not sure putting the adaptative logic in HTML is the best way to solve this.
      Anyway, both solutions will need new tags or meta, and some enhancements from the speculative parser.

    2. You’re not the first one today suggesting an opt-out from the speculative parser. :) I’m sorry, but it does not sound like progress to me. In many cases responsive images without speculative parser might be slower than large images with speculative parsing and preloading.

      I think you’re searching for a solution that will always download the optimal image, and for such a solution it is true that the download of the image must wait until the DOM is properly constructed, CSS applied and all scripts ran. But in terms of downloading resource, that’d bring us back to the dark ages of IE6/7 :) (with slightly more connections per host)

      IMO, a « good enough » solution that simply downloads smaller images for small screens might do. The author might have more work (determining the maximum presentation sizes for each resolution media query) but it stays in the current scope of responsive design.
      Any proposal that is media queries based (where the URL is in the HTML) can be properly handled by the speculative parser (since the browser has all the info when it encounters the image). Bruce Lawson’s proposal ( http://www.brucelawson.co.uk/2011/notes-on-adaptive-images-yet-again/ ) looks OK, even if it’s far from being DRY.

    3. In your opinion, would it be possible to enhance this secondary parser/downloader to stop the image download once the browser acknowledge that the image will finally use another address ?

      As far as my test went when I did some research many months ago about speculative downloads, this is not the case (the URL found will be downloaded whatever the page becomes once CSS and Javascript is executed).
      It may be a good enhancement : In that case the secondary parser may be helpful, but will never be really harmful (which it is sometimes now).

      I am really not a fan of the idea to put presentational logic based on screen size directly on HTML. Which image I want is certainly often based on the viewport size, but also on what style I choose. I would very dislike having to modify all html template for a style change, or having to update multiple files in multiple languages for style update.

      Ok, I know, I already have to modify HTML each time a have a real redesign, but at this time, I successfully manage to have to different jobs. This may not be the case anymore with the HTML solution. The overall complexity may deteriorate.

    4. Ideally, IMG tag images should be part of the content, while styling images should be CSS background images. We can already control which background images we download using media queries, so I believe that the styling issue can be solved that way. (At least in theory. In real life the line between content and style is sometimes not very clear…)

      Regarding the speculative parser terminating in-flight requests, Eric Lawrence said that IE9 does that ( http://blogs.msdn.com/b/ieinternals/archive/2011/07/18/optimal-html-head-ordering-to-avoid-parser-restarts-redownloads-and-improve-performance.aspx ) when the base tag is dynamically changed. I’m not sure if it’s done when a specific image changes URL, but I guess that it is possible. OTOH, once we start relying on that we suffer from the same race conditions that current hacks are suffering from.

      I don’t see a particular problem with adding presentational logic (in the form of media attributes) to various HTML elements. Why is it bad? What am I missing?

  3. Pour mon code, l’idée c’est de dire :

    Les images de classe « intro », remplace les, dans l’ordre, par :
    – le contenu dont l’url est dans l’attribut « data-big » si l’image doit être affichée avec une largeur supérieure à 300px
    – le contenu dont l’url est dans l’attribut « data-small » si l’image doit être affichée avec une hauteur inférieure à 50px
    – sinon on affiche le contenu dont l’url est dans l’attribut « src » (l’image quoi)

    La seconde partie c’est du CSS tout classique qui détermine des hauteurs et largeurs, et du coup change l’url qui est utilisée à cause de la règle plus haut.

    L’avantage du bazar c’est que pour ce que j’en ai vu (mais 90% de chances que j’ai loupé quelque chose), ça respecte la grammaire de base CSS et c’est rétrocompatible (si ce n’est pas supporté, rien ne change).

    Note que ma solution ne règle pas le problème du double download, qui AMHA doit pour le coup être réglé côté HTML, avec un attribut ou un meta qui limite l’analyse prédictive.

  4. Très bon billet. J’approuve absolument tout.
    Ça me parait l’évidence et l’élégance même.

    Moi j’aurais rajouté une directive CSS du genre @imgalt, mais finalement tu as raison, ce n’est même pas tellement nécessaire.

    Si jamais le navigateur connais la bande passante, il peut faire des requêtes HTTP HEAD pour connaitre la taille des images alternatives, et décider lui même en fonction de ces poids. (Pour éviter les requêtes, on pourrait même imaginer de les déclarer dans la CSS)

  5. Un point qui n’est pas abordé : ce n’est pas parce qu’une image est plus grande qu’elle est significativement plus grosse ! Dans ma jeunesse, Real était une des solutions préférées pour le streaming vidéo car le serveur était capable de changer la qualité de la compression à la volée (bon, si on avait choisi d’encoder en multi-taux évidemment) et donc l’occupation de la bande passante. Dans ma jeunesse toujours, on encodait les JPEG en « progressive rendering » comme ça le navigateur pouvait choisir d’arrêter le chargement si ça prenait trop de temps, mais on avait quand même l’image complète (en basse qualité, ce qui est différent de basse définition). Tout ça pour dire, c’est bien de penser à la taille, mais il ne faut pas oublier le poids :-)

    1. Il me manque le mot le plus intéressant de ton commentaire mais je suppose que tu parles de noscript.

      Pour moi ça a plusieurs défauts :

      – Côté perf, soit on a un double téléchargement, soit ça coupe le moteur de préanalyse
      – C’est moche et complexe (oui, je sais, c’est un argument non technique mais ça joue)
      – Ca implique de gérer la logique de présentation dans la HTML

      Plus exactement, imaginons que quand l’écran fait 1024 de large, j’ai une colonne à gauche de 300px. Mon image principale devra être à 700px de largeur. Si mon écran fait seulement 800px de large, alors je fait passer la colonne gauche en bas de page et je peux utiliser mes 800px pour mon image principale.

      Ca ne pose pas du tout de problème technique à implémenter avec ta solution à base de noscript mais ça veut dire de coder cette logique de présentation sur « quelle est la taille disponible dans quel cas » et côté css et côté html. Le jour où je souhaite adapter/modifier le graphisme ou l’agencement, il faudra que je pense aussi à impacter toutes mes occurences HTML. Il faudra que je m’assure de ne pas être incohérent entre les deux. Plus gênant : il me sera aussi impossible de partager un même code HTML pour deux styles différents vu que les deux deviennent totalement liés.

    2. Je parlais effectivement de l’utilisation de noscript. ^^

      Merci pour le complément, j’y vois un peu plus clair. :o)

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *