Veien til detaljrike og responsive bilder som laster raskt

La oss si at vi har et fotografi som inngår i en artikkel som publiseres på nett. Da er det er viktig at fotografiet ser eksemplarisk ut, uavhengig av hvilken enhet brukerne benytter til å lese artikkelen.

I denne artikkelen går jeg gjennom hvilken HTML- og CSS-kode du må skrive for å oppnå både detaljrike og responsive bilder, som i tillegg laster raskt. Artikkelen er delt inn i følgende steg:

Du kan også hoppe rett til oppsummeringen, for å få en kortfattet oversikt.

Det er laget en egen side med eksempler som du kan følge mens du leser artikkelen.


Legg inn bildet

I denne artikkelen vil vi bruke et fotografi tatt av Timur Romanov, hentet fra Unsplash.

Et bilde av en hest som spiser gress
Dette bildet vil vi bruke som et eksempel gjennom hele artikkelen.

Første steg er å sette inn bildet ved å bruke <img>-elementet. For å gjøre eksempelet enklere, sier vi at dette bildet er dekorativt, og setter den alternative teksten til en tom streng.

Siden vi kommer til å jobbe med det samme bildet i flere størrelser, legger jeg til bildebredden i piksler (1080) som en del av filnavnet.

<img src="horse_1080.jpg" alt="">

Eksempel 1

Kodesnutten ovenfor er enkel, og antageligvis velkjent, men den har en svakhet. Når bildet lastes ned, vil du oppleve at nettsidens layout hopper.

En animasjon som visualiserer at nettsidens layout hopper når bildet lastes.
Layouten hopper når bildet lastes.

Nettleseren kjenner til bildets dimensjoner først etter at det er lastet ned. og vil frem til da ikke kunne reservere plass til det.

For å unngå dette, kan du angi bildets dimensjoner ved å legge til attributtene width og height. Da vet nettleseren på forhånd hvor stort bildet er, og reserverer nødvendig plass.

<img src="horse_1080.jpg" width="1080" height="720" alt="">

Eksempel 2


Gjør bildet responsivt

Akkurat nå har bildet en fast bredde. Vi ønsker at bildet skal være responsivt, og krympe dersom nettleserbredden er smalere enn den opprinnelige bildestørrelsen.

Vi løser dette ved å legge til CSS-deklarasjonen max-width: 100%;, som forteller at bildet ikke skal ta opp mer horisontal plass enn det som til enhver tid er tilgjengelig.

Vi må også legge til height: auto;, for at bildet skal bevare sine proporsjoner når bredden reduseres. Dette gjør vi for å overstyre den høyden vi satte i sted ved hjelp av height-attributtet.

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

Denne kodesnutten med CSS sørger for at bildet blir responsivt, men introduserer samtidig det problemet vi hadde tidligere: Layouten hopper når bildet lastes, fordi height: auto; overstyrer height="720" når nettleserbredden er smalere enn bildets opprinnelige bredde.

Les artikkelen Setting Height And Width On Images Is Important Again for å få en mer utfyllende forklaring på hva som skjer her.

For å unngå at layouten hopper i disse tilfellene, kan vi benytte den relativt ferske CSS-egenskapen aspect-ratio for å angi størrelsesforholdet mellom bildets bredde og høyde. Da klarer nettleseren å beregne hvor stor vertikal plass bildet kommer til å oppta, selv før bildefilen er lastet.

img {
  max-width: 100%; 
  height: auto;
  aspect-ratio: 3 / 2; 
}

Eksempel 3

Moderne nettlesere kalkulerer aspect-ratio automatisk, dersom attributtene height og width er satt.


Legg til en versjon med høy oppløsning

CSS-pikselen (px) er en absolutt enhet som er forankret i en fysisk størrelse. Om du leser en skjerm på en armlengdes avstand, er 1px definert til å være omtrent 0,26 millimeter (1/96 tomme).

Høyoppløselige skjermer består av fysiske piksler som er vesentlig mindre enn dette. En mobiltelefon har gjerne en oppløsning bestående av 1440x2560 fysiske piksler, som tilsvarer 360x640 CSS-piksler. Skjermen viser altså 16 (4x4) piksler for hver CSS-piksel du forholder deg til i nettleseren.

Les mer om CSS-piksler, pikseltetthet og fysiske størrelser.

Illustrasjon som visualiserer forskjellen mellom ordinære og høyoppløselige bilder.
Bildet til høyre ser vesentlig skapere ut enn det til venstre.

Vi ønsker å utnytte hver eneste fysiske skjermpiksel til høyoppløselige skjermer, slik at bildene fremstår som ekstra skarpe.

HTML-attributten srcset, som legges til <img>-elementet, lar deg angi alternative bildefiler. Du angir adressen til en bildefil og et (frivillig) kriterium som gir et hint om når denne bildefilen skal brukes. Det er mulig å angi flere bildefiler i samme srcset-attributt, separert av komma.

I vårt tilfelle legger vi til srcset="horse_2160.jpg 2x", som forteller at horse_2160.jpg har dobbel så høy pikseltetthet som det opprinnelige bildet. Nettleseren vil da automatisk velge å benytte den bildefilen som samsvarer best med skjermens fysiske oppløsning.

<img src="horse_1080.jpg" srcset="horse_2160.jpg 2x" 
  width="1080" height="720" alt="">

Eksempel 4

Det er støtte for andre kriterier enn pikseltetthet (x) i srcset. Bredde (w) kan også brukes, gjerne i tospann med sizes-attributtet.


Lever ulike bilder til ulike oppløsninger

Vi har tidligere valgt at bildet skal være responsivt og tilpasses etter nettleserbredden. Dette fører naturlig nok til at bildet blir temmelig lite på små skjermer.

En liten versjon av bildet av en hest som spiser gress
Når bildet forminskes, forminskes også hesten.

Bildet blir faktisk så lite at hovedmotivet vårt, hesten, forsvinner litt. I dette tilfellet kan det passe bedre å kun vise et utsnitt av det opprinnelige motivet, slik at vi ser mer hest, og mindre skog og eng.

Et zoomet inn bilde av en hest som spiser gress
Dersom vi endrer utsnittet, blir hesten tydeligere.

Dette kan vi oppnå ved innføre to HTML-elementer:

Ved første øyekast, minner dette litt om å bruke <img> kombinert med srcset. Grunnen til at vi heller bruker <picture> og <source>, er at <source>-elementet har støtte for media queries, slik at vi kan levere ulike bilder til ulike oppløsninger.

Bruk srcset for å angi én eller flere bildefiler, og media for å legge til et media query. I eksempelet nedenfor vil horse_small.jpg brukes dersom nettleserbredden er 540px eller mindre. Hvis dette kriteriet ikke er sant, vil standardvalget horse.jpg brukes.

<picture>
  <source srcset="horse_small_540.jpg, horse_small_1080.jpg 2x" 
    media="(max-width: 540px)">
  <img src="horse_1080.jpg" srcset="horse_2160.jpg 2x" 
    width="1080" height="720" alt="">
</picture>

Eksempel 5

Det er et krav om at <picture> skal inneholde nøyaktig ett <img>-element. Vær også oppmerksom på at attributtene width, height, alt og src kun skal angis på <img>-elementet, og ikke på noen av <source>-elementene.


Reduser bildets filstørrelse

Ytelse er en sentral del av brukeropplevelsen. Vi vil at det skal gå kortest mulig tid fra en nettside åpnes, til den er ferdiglastet og klar for interaksjon fra brukeren. Dette er i utgangspunktet et primitivt regnestykke: Hver kilobyte som må lastes ned, behandles og tegnes av nettleseren, påvirker ytelsen negativt.

Bilder utgjør en vesentlig del av nettsiders tyngde, og vi bør gjøre det vi kan for å redusere filstørrelsen på dem. I følge HTTP Archive sin rapport om nettsidetyngde, veier en gjennomsnittlig nettside 2038,4 kB, der 948,1 kB av dem er bilder.

JPEG er det mest brukte filformatet for å vise fotografier på web. En høykvalitets JPEG-fil tar mye diskplass, men heldigvis kan du vanligvis redusere kvaliteten flere hakk, uten at det går utover brukeropplevelsen.

Under skrivingen av denne artikkelen brukte jeg webapplikasjonen Squoosh til redusere filstørrelsen til alle bilder. Fotografiet som er inkludert i første kapittel hadde i utgangspunktet en filstørrelse på 1 300 kB. Etter å ha brukt Squoosh, uten å justere standardinnstillingene, ble filstørrelsen redusert med 89 % til 148 kB.

Komprimer bilder med Squoosh.

Filstørrelsen kan reduseres ytterligere, om vi bruker filformater med mer effektive komprimeringsalgoritmer. WebP og AVIF er de mest aktuelle alternativene til JPEG for bruk i fotografier.

Jake Archibald har gjort en grundig sammenligning av bildekvalitet og filstørrelse for JPEG, WebP og AVIF . Som en tommelfingerregel kan vi si at en AVIF-fil er halvparten så stor som en JPEG-fil med tilsvarende bildekvalitet. Størrelsen på en WebP-fil ligger omtrent midt mellom AVIF og JPEG.

Ved å bruke <picture> og <source> kan vi levere alternative bildeformater til nettleserne som støtter dem, ved å bruke et type-attributt. Dersom vi vil tilby bilder på AVIF-format i tillegg til JPEG-format, legger vi til ekstra <source>-elementer med type="image/avif", i tillegg til de attributtene vi lærte om tidligere.

Vi har også et annet triks på lur, «lazy loading», som vil si at et bilde ikke lastes ned før det er nødvendig. Dette gjøres ved å legge til et loading-attributt med verdien "lazy"<img>-elementet.

<picture>
  <source srcset="horse_small_540.avif, horse_small_1080.avif 2x" 
    type="image/avif" media="(max-width: 540px)">
  <source srcset="horse_1080.avif, horse_2160.avif 2x" 
    type="image/avif">
  <source srcset="horse_small_540.jpg, horse_small_1080.jpg 2x" 
    media="(max-width: 540px)">
  <img src="horse_1080.jpg" srcset="horse_2160.jpg 2x" 
    width="1080" height="720" loading="lazy" alt="">
</picture>

Eksempel 6


Vær trygg på nettleserstøtte

Flere av de teknikkene som brukes i denne artikkelen er relativt nye. Bildeformatet AVIF ble for eksempel implementert i Google Chrome i august 2020, mens støtte for aspect-ratio kom først i januar 2021.

Det du imidlertid kan være trygg på, er at det vil vises et bilde av en hest i alle grafiske nettlesere. Kodeeksempelet vi har bygd opp stein for stein så langt, følger prinsippet «progressive enhancement». Vi gir alle brukere en god opplevelse i bunn, og tilbyr noe ekstra til de nettlesere som har støtte for det.

Dersom du bruker en eldre nettleser uten AVIF-støtte, vil du for eksempel få servert en JPEG-fil. Dette bildet tar litt lenger tid å laste ned, men brukeropplevelsen er fortsatt god.

Nedenfor følger en liste med lenker til nettstedet caniuse.com, hvor du kan se hvor god nettleserstøtte det er for de teknikkene som er brukt i denne artikkelen:


Oppsummering

Om dere ønsker et detaljrikt og responsivt bilde som laster raskt, kan dere følge denne oppskriften:

↑ Hopp til starten av artikkelen