Il y a eu des centaines d’articles techniques détaillés et plus ou moins smart sur la possibilité de télécharger une image plus ou moins grosse suivant la taille d’affichage, afin de ne pas utiliser une énorme image sur mobile ou une ridiculement 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 probablement l’article de Bruce Lawson. Il faut aussi lire les commentaires.
Tout d’abord oubliez les astuces à base de javascript et de noscript. Il existe des machins horribles qui résistent à peu près à tout, mais ça reste franchement bancal. Oubliez encore plus les scripts à base de cookie, qui de toutes façons ne pourront jamais répondre à plus du tiers de la problématique, et encore, avec des effets de bords.
Bruce part d’une solution uniquement basée sur des CSS, qui de plus à la bonne idée d’être théoriquement déjà fonctionnelle. Il suffirait d’améliorer le support CSS 3 des navigateurs pour que cela ne se pose plus. Pour l’instant cela n’est possible qu’avec Opera et Chrome, et les optimisations de performance de ces navigateurs risquent de faire télécharger deux images au lieu d’une seule (ce qui est un peu l’opposé du but recherché).
Il propose ensuite un marquage HTML pour arriver au même résultat. C’est rétro-compatible avec les navigateurs actuels, et ne devrait pas être impossible à implémenter.
Maintenant ça ne me plait pas
Tout d’abord le marquage HTML me semble le mauvais endroit pour résoudre la problématique. On parle de répondre à des tailles d’affichage, et ça c’est typiquement une question de présentation, donc de CSS. Certainement il y a des fois où un marquage HTML aura du sens, mais selon moi ce sera un cas particulier du cas général, et utiliser HTML est prendre le problème par le mauvais sens.
Ensuite il y a des problématiques qui marquent un manque de recul (pas de la personne, mais bien en rapport avec les besoins réels et les capacités des navigateurs). Filtrer sur le fait que l’utilisateur est en 3G est seulement impossible pour beaucoup de situations (le navigateur n’a pas l’information), 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 alternative acceptable (par exemple à cause des pertes de paquets à gogo), soit être à 7Mb/s et crâner devant la majorité des liaisons ADSL de mon pays (qui est pourtant très bien connecté). De toutes façons la vitesse de connexion sur mon propre accès est un très mauvais révélateur de la vitesse réellement disponible pour joindre le serveur d’en face. Le réseau peut être encombré chez moi, chez mon FAI, sur le serveur d’en face, ou n’importe où au milieu.
Je seconde le commentaire numéro 8 : s’il fallait vraiment tailler le contenu de manière fixe en fonction uniquement de taille d’écran et de vitesse de connexion, une déclaration dans les entêtes de la requête et une négociation HTTP seraient bien plus efficaces. L’option a de plus l’avantage de ne poser aucun problème de compatibilité arrière.
La problématique de base
Toutefois, on revient au problème initial. À force de discuter certains ont oublié la problématique de base : choisir une image en fonction de la taille à afficher. Le mécanisme éventuel ne doit prévoir que ça : permettre de spécifier différentes adresses (ou différents suffixes) en fonction de différentes hauteurs ou largeurs.
Charge à vous d’utiliser une entête ou l’adresse IP côté serveur pour vérifier 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’utiliser des @media pour proposer plusieurs versions en fonction de la taille de l’écran ou de son orientation, ou de contraindre cette taille en fonction. En combinant tout cela vous devriez pouvoir faire tout ce qui vous amuse, mais la problématique qui nous manque c’est uniquement celle de fournir plusieurs URLs en fonction de la taille prévue pour l’affichage. De toutes façons on ne trouvera jamais de solution qui fait le café.
Je n’ai pas « la » solution, mais selon moi (et je rejoins beaucoup le commentaire 15) :
- La réponse principale doit être côté CSS (quitte à avoir d’autres types de réponses ailleurs pour des cas niches)
- Elle ne doit s’occuper que de proposer des images alternatives en fonction de la largeur ou hauteur prévue pour afficher l’image (et non de la taille du viewport ou d’autres paramètres tiers)
- Elle ne doit pas provoquer de double téléchargement sur les navigateurs non compatibles
- Elle doit avoir un fallback acceptable sur les navigateurs non compatibles
Le reste se fait avec les outils existants, pas en les remplaçant.
Le jeu de « ma solution à moi »
Si vraiment je devais créer quelque chose à chaud (ce qui se révèlera forcément une erreur), j’aurai 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 condition n’est probablement pas celui qu’il faut retenir mais il est volontairement basé sur un jeu de mot clefs limité et des contraintes qui le sont tout autant (hauteur et largeur disponibles, c’est tout). On peut tout à fait envisager que cela ne fonctionne que si les tailles width ou height sont déterminées explicitement dans la CSS, histoire de ne pas rendre l’implémentation irréalisable. Autre avantage : c’est à priori compatible avec l’existant puisqu’au pire si content-if n’est pas supporté, c’est toute la règle qui est ignorée. Reste au navigateur qui supporte ça de prendre la première version qui correspond.
15 réponses à “Responsive image”
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
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.
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.
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.
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.
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.
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?
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.
Et j’en rajoute une couche.
http://my.opera.com/karlcow/blog/2011/12/08/responsive-images-and-transparent-content-negotiation-in-http
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)
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 :-)
J’arrive un peu après la guerre, mais j’ai du mal à comprendre pourquoi il est déconseillé d’utiliser les astuces à base de . J’avoue que j’avais été assez séduite par cette proposition là : http://www.monoliitti.com/images/.
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.
Je parlais effectivement de l’utilisation de noscript. ^^
Merci pour le complément, j’y vois un peu plus clair. :o)
Les « cas niches » : le style d’Éric a quand même du chien.
(je sais que je n’alimente pas constructivement le débat, je sais, je sais) ;)