Niveau Niveau confirmé

Responsive Images : comprendre srcset et sizes

Articleresponsive

Publié par le , mis à jour le (303168 lectures)

performance responsive background-image srcset picture

Entre les images floues sur écrans rétina, mal adaptées ou trop lourdes, il n'est pas toujours évident de gérer les différentes ressources graphiques sur l'ensemble des surfaces et des périphériques. Les techniques de Responsive Web Design tiennent parfois (souvent) de la bidouille et il est difficile de distinguer les bonnes pratiques d'intégration.

Au sein des différentes spécifications existantes et des techniques utilisables en production, srcset, sizes et <picture> sortent du lot aujourd'hui. Dans cet article, nous allons tenter de mieux comprendre comment fonctionne vraiment srcset.

Note : cet article a été initialement rédigé en avril 2014 par Geoffrey Crofte. Il a subi une refonte intégrale en mai 2017 pour se mettre à jour et s'étoffer considérablement.

Images fluides, l'échauffement

Lorsque vous insérez une image dans votre page web, il y a de fortes chances pour que vous utilisiez un code semblable à :

<img src="mon-image.jpg" width="850" height="475" alt="">

Ce code vous permet de charger une image en réservant un espace de 850 pixels de large aussi bien sur un écran classique que sur un écran de smartphone. Avec un peu de prévoyance, vous avez un code CSS bien construit qui vous permet de redimensionner l'image pour éviter les débordements.

img {
  max-width: 100%;
  height: auto;
}

Mais vous conviendrez que charger une image de 850 x 475 px n'est pas très adapté sur un écran de 320 px de large (si on prend comme exemple la largeur actuelle gérée par l'iPhone). Une image de cette taille pèse environ 150 Ko (compression de 40% sur une photographie), là où une image adaptée de 320 x 179 px ferait environ 23 Ko avec la même compression. Soit une différence de 127 Ko, multiplié par le nombre d'images du même genre dans votre page.

srcset, pour piocher la bonne ressource

L'attribut srcset vient apporter une solution à ce problème en vous permettant de définir une image adaptée au terminal de consultation en ciblant la taille de l'écran, et également la densité de pixels

Au sein de cet attribut srcset, les spécifications prévoient deux types de descripteurs permettant de conditionner le chargement des ressources :

  • le descripteur "x" sélectionne l'image en fonction de la densité de pixel (pixel ratio) de l'écran
  • le descripteur "w" sélectionne l'image en fonction de sa taille et de l'adéquation à la surface de l'écran

srcset et le descripteur "x"

Le descripteur "x", appelé officiellement "pixel density descriptor" correspond - sans surprise - au pixel ratio de l'écran. C'est un bon moyen de cibler les écrans dits rétina (pixel-ratio 2) ou HD notamment.

Le nombre exprimé dans ce descripteur peut contenir des décimales mais doit forcément être positif. La valeur par défaut de ce descripteur, s'il est absent est "1x".

Voici par exemple une utilisation envisageable de l'attribut srcset combiné au descripteur "x" :

<img src="mon-image.jpg" srcset="mon-imageHD.jpg 2x" alt="">

Dans cet exemple, voici ce qui se passe :

  • si le navigateur ne reconnaît pas srcset, il charge et affiche l'image proposée dans l'attribut src
  • sinon, dans le cas où le navigateur se sent proche d'un pixel-ratio de 2, il chargera mon-imageHD.jpg et sinon il choisira mon-image.jpg.

srcset et le descripteur "w"

Le descripteur "w", pour "width descriptor" informe le navigateur de la largeur de l'image proposée. La valeur précédant "w" désigne bien la taille de l'image et non du viewport ou de la surface de l'écran.

Voici par exemple une utilisation envisageable de l'attribut srcset combiné au descripteur "w" :

<img src="mon-image.jpg"
     srcset="mon-image-320.jpg 320w,
             mon-image-640.jpg 640w"
     alt="">

Dans cet exemple, j'indique clairement au navigateur : "je dispose de deux images dont la première a une largeur de 320px et l'autre 640px, débrouille-toi avec ça pour piocher la meilleure".

Selon les spécifications, le navigateur va devoir tenir compte de plusieurs paramètres dans son choix :

L’agent utilisateur va calculer la densité de pixel réelle de chaque image à partir du descripteur w spécifié et des tailles de rendus spécifiées dans l’attribut sizes. Il peut ensuite choisir n’importe quelle ressource en fonction de la densité de pixels de l’écran de l’utilisateur, de son niveau de zoom, ou peut-être d’autres facteurs comme les conditions de réseau de l’utilisateur.

En gros, si l'utilisateur dispose d'un device de 320px de large, l'image chargée sera mon-image-320.jpg; Par contre, sur une surface plus vaste, le choix risque de se porter plutôt sur mon-image-640.jpg.

Les choses se compliquent un peu en sachant que si descripteur "w" est employé, alors l'attribut sizes s'applique forcément.

Notez qu'il n'est pas autorisé de combiner un descripteur "x" et un descripteur "w" dans la même condition d'un srcset.

srcset et l'attribut sizes

L'attribut sizes permet d'indiquer au navigateur quelle doit être la largeur finale de l'image à l'affichage. 

Par exemple : 

  • sizes="1000px" précise au navigateur que l'image au final occupera 1000px de large sur l'écran
  • sizes="100vw" indique que l'image choisie devra occuper toute la largeur du viewport

Il s'agit d'une information essentielle pour permettre au navigateur de choisir la bonne ressource, car il devra jongler entre sa largeur effective, la largeur souhaitée de l'image mais aussi de sa densité de pixel.

Voici par exemple une utilisation envisageable de l'attribut srcset combiné au descripteur "w" et à l'attribut sizes :

<img src="mon-image.jpg"
     srcset="mon-image-320.jpg 320w,
             mon-image-640.jpg 640w"
      sizes="100vw"
      alt="">

Dans cet exemple, nous indiquons au navigateur que l'image devra au final occuper toute la surface. Son choix risque d'être celui-ci :

  • un iPhone 3 dont la taille est de 320px chargera mon-image-320.jpg
  • un iPhone 4 dont la taille est de 320px mais où la densité de pixels est de 2 chargera mon-image-640.jpg

Notez les informations complémentaires suivantes : 

  • Il n'est pas autorisé d'employer l'unité % en valeur de sizes (les unités courantes sont vw ou px)
  • la valeur par défaut de sizes est 100vw
  • si sizes est présent dans l'élément <img>, alors le descripteur "w" est obligatoire 

Démo de srcset

Image affichée VS image(s) chargée(s) ?

L'un des intérêt de cet attribut est qu'il prenne la place de l'attribut src si l'un des critères est validé, l'avantage étant, de ce fait, de ne charger que l'image la plus intéressante pour le terminal qui consulte votre page web. D'ailleurs le processus est détaillé sur la page du W3C.

Figurez-vous que c'est exactement ce que font les navigateurs actuels (desktop et mobile).

Mais comment réagit le navigateur s'il rencontre deux images dont les critères correspondent ?
Très bonne question, nous nous la sommes posée. Pour effectuer le test, nous avons regroupé plusieurs images identiques dans leurs proportions et ajouté un mot dessus pour détecter rapidement laquelle est chargée. Puis nous avons appliqué des critères bidons et des critères correspondant à notre terminal de test (viewport 1920px, densité de 1.0).

Test N°1 : La densité
<img src="mon-image.jpg"
     srcset="mon-image-big.jpg 1x,
             mon-image-hd.jpg 2x"
     width="850" height="475" alt="">

L'image "big" correspondant au critère "1x" est chargée, donc tout va bien.

Test N°2 : L'absent
<img src="mon-image.jpg"
     srcset="mon-image-hd.jpg 2x"
     width="850" height="475" alt="">

Ici aucun critère de srcset ne correspond, l'image du src est chargée.

Test N°3 : Le viewport
<img src="mon-image.jpg"
     srcset="mon-image-320.jpg 320w,
             mon-image-big.jpg 1920w"
     width="850" height="475" alt="">

L'image "big" est chargée, ce qui est le comportement attendu dans notre cas. 

Il est important de retenir que srcset offre au navigateur une information sur les images et leur utilisation attendue. Une image de 500px (500w) de large proposée à un iPhone 4S (viewport 320px avec ratio de 2) ne satisfait pas pleinement le ratio de 2, il se pourrait donc que le terminal s'oriente vers une image plus grande si vous en proposez d'autres.

Support et limites

Le support de srcset est aujourd’hui assez large.

Navigateurs Versions supportées Détails / Commentaires
Internet Explorer Internet Explorer Non supporté sur Internet Explorer
Supporté depuis Edge 15
Firefox Firefox 38+
Firefox Mobile

OK
OK

Chrome Chrome 38+
Chrome Mobile

OK
OK
Opera Opera 25+
Opera Mobile
OK
OK
Safari

Safari 7.1+
Safari Mobile 8.0+

Support partiel avant Safari 9, complet ensuite
Support partiel avant Safari Mobile 9.2, complet ensuite
Android Browser Android Browser 
Chrome for Android
Pas de support Android Browser
Support depuis Chrome Mobile

3 octobre 2014 : cet article a été mis à jour pour correspondre à la dernière version de la spécification et des supports. Merci à HTeuMeuLeu pour son coup de pied aux fesses pour la mise à jour de cet article

5 mars 2015 : mise à jour de l'article suite au passage officiel de srcset dans la spécification HTML5, mise à jour du tableau de support 

Commentaires

Article excellent :)
Mais d'un côté vue la situation actuelle c'est un peu con.
C'est un attribut qui serait excellent pour les mobiles et divers autres supports. Alors que pour l'instant il n'y a que la toute dernière version de Chrome version bureau qui le gère. Et j'ai pas souvent un écran 5 pouces sur mon PC portable ou fixe.
Par contre, dès que les navigateurs mobile l'implémenteront ça sera super :)

Je gérais ça avec les média queries, mais donc ce n'était pas une balise "img"
Dans l'idée, je me demandais si la gestion de tailles d'image n'avait pas plus sa place dans le CSS ( gestion de Style ) ? ( c'est une question que je me pose ... )

Merci pour vos commentaires.

@Jconline : On vient de me préciser que la dernière version de Chrome Mobile (34 également) supporte srcset et les critères de densité de pixels. :) (merci @eQRoeil sur Twitter)

@sizvix : d'un point de vue visuel oui, mais là on parle d'une possibilité de distribuer la bonne image au bon terminal (suivant ses capacités d'affichage). C'est davantage un problème de performance qu'un problème de style. Après CSS peu intervenir en complément pour gérer les problèmes de débordement, mais ce sont deux choses distinctes.

Génial. Maintenant il va juste falloir attendre 5 ans pour en tirer avantage. Encore prématuré pour l'implémentation en prod', mais merci pour la veille techno.

@Olivier C : le seul point qui m'intéresserait serait le support des critères de viewport, et pour le moment c'est un peu caca... du coup oui, il va falloir attendre un peu. (5 ans je ne pense pas :p)

Si 2x c'est une limite haute, ça veut dire que les devices 3x (comme le Galaxy S4, le Nexus 5, etc...) chargeront l'image par défaut ?

@Didodu258 : et pour les utilisateurs sans JS ? Pas d'image ?

@deejay-bee : une fois que le support sera meilleur, des solutions vont naître, je ne me fais pas de souci pour cela :)

Toute mes excuses, ah l'autocorrection je voulais dire intéressant bien entendu !
ça serait tout de même sympa de pouvoir rééditer son message après envoi.

Bonjour,

Merci pour cet article ! Quelqu'un sait-il comment se comporte un navigateur qui ne supporte pas la balise picture ? Y a-t-il une astuce pour utiliser picture et fournir un contenu alternatif aux anciens navigateurs ? Comme IE10 et 11 n'acceptent plus les commentaires conditionnels, c'est devenu compliqué de les cibler.