content-visibility: rendering utenfor skjermen uten listevirtualisering
Listen med tusen kommentarer under en artikkel rendrer merkbart sakte, og scrollingen gjennom den hakker på en svakere telefon. Standardsvaret i React er virtualisering – react-window eller @tanstack/react-virtual – som bare rendrer de elementene som faktisk får plass i det synlige vinduet, pluss en liten buffer, til DOM-en. Det fungerer, men har en kostnad det sjelden snakkes om ved implementering: elementene utenfor vinduet er ikke skjult, de eksisterer ikke i DOM-en. Å trykke Ctrl+F finner ikke teksten i kommentar #850 før du har scrollet dit manuelt. Utskrift av siden viser bare det som faktisk var rendret i det øyeblikket utskriften ble utløst. Skjermleser-navigasjon gjennom overskrifter eller landemerker hopper over innhold som fysisk ikke eksisterer i treet – dette er ikke en feil ved virtualisering, det er en uunngåelig konsekvens av den.
content-visibility angriper det samme ytelsesproblemet fra en helt annen vinkel: elementene blir værende i DOM-en, alltid, i sin helhet. Nettleseren hopper rett og slett over arbeidet den normalt måtte gjøre – layout, maling, oppretting av kompositeringslag – helt til elementet kommer nær nok det synlige området til at det arbeidet faktisk gir mening.
Hva «hopper over arbeidet» egentlig betyr
content-visibility: auto på et element tvinger nettleseren til å anvende CSS Containment på det – nøyaktig samme mekanisme som i artikkelen om Container Queries lot deg unngå en løkke ved spørring om beholderens størrelse. Her tjener containment et annet formål: hvis elementet er utenfor skjermen, kan nettleseren trygt anta at dets interne layout og maling ikke påvirker noe utenfor det selv, så den kan utsette det hele, uten å regne ut en eneste piksel inni.
css
.comment {content-visibility: auto;contain-intrinsic-size: auto 180px;}
contain-intrinsic-size er ikke et kosmetisk tillegg – uten det ville et hoppet-over element krympe til null høyde, fordi nettleseren ikke har noe sted å hente størrelsen fra når den ikke regner ut layout inni. Dette er nøyaktig samme problem som med container-type: size uten eksplisitt høyde – bare at konsekvensen her ikke er et forsvinnende kort, men en hoppende scrollbar, fordi dokumentets høyde plutselig endrer seg hver gang enda en kommentar går inn i eller ut av hoppet-over-modus. Verdien auto 180px forteller nettleseren to ting samtidig: bruk 180px som første tilnærming, før elementet i det hele tatt er reelt rendret én gang, og husk deretter dets faktiske, beregnede størrelse, og bruk den lagrede verdien som plassholder ved hver etterfølgende hopping – så hvis de reelle kommentarene har ulik høyde, blir nettleserens gjetning på hvor mye plass som skal reserveres, gradvis mer presis, i stedet for å holde stivt fast ved ett tall.
Trikset du ikke får med full virtualisering: «hidden until found»
Dette er stedet der content-visibility gjør noe virtualisering ikke kan gjøre i prinsippet – ikke bare i praksis. Et element i auto-modus har fortsatt teksten sin fysisk i DOM-en, selv om det er hoppet over rendringsmessig. Nettleseren vet dette og behandler et slikt element som «skjult, men søkbart» (hidden but matchable): når brukeren trykker Ctrl+F og skriver inn en frase som matcher tekst inni et hoppet-over element, opphever nettleseren midlertidig hoppingen, rendrer elementet for ekte, fremhever treffet og scroller til det – automatisk, uten en linje kode fra din side. display: none har aldri gjort dette (Ctrl+F ignorerer det rett og slett), og en virtualisert liste kan ikke gjøre det, fordi teksten du søker etter, rett og slett ikke finnes i DOM-en før du har scrollet dit manuelt.
Praktisk eksempel: en kommentarliste uten en eneste linje JS for virtualisering
jsx
const CommentList = ({ comments }) => (<ul className="comment-list">{comments.map((comment) => (<li key={comment.id} className="comment"><p className="comment__author">{comment.author}</p><p className="comment__body">{comment.body}</p></li>))}</ul>);export default CommentList;
css
.comment {content-visibility: auto;contain-intrinsic-size: auto 180px;padding: 12px 0;border-bottom: 1px solid #e5e5e5;}
Hele komponenten rendrer alle de tusen <li>-ene til DOM-en – ingen logikk for scrollvindu, ingen utregning av hvilke elementer som akkurat er synlige, ingen bibliotek. Nettleseren avgjør på egen hånd hvilke av de tusen kommentarene det er verdt å regne ut layout og maling for akkurat nå, og hvilke som rent rendringsmessig «ikke eksisterer», selv om de finnes i sin helhet i DOM-en.
Når det ikke er nok – og hvorfor virtualisering fortsatt gir mening
content-visibility løser kostnaden ved rendering – layout og maling. Den løser ikke kostnaden som ofte gjør mest vondt i en React-applikasjon: hver av de tusen <li>-ene er fortsatt en reell React-komponent som monteres, hydreres og kan ha sine egne hooks og effekter. Å hoppe over CSS-rendring gjør ikke at de tusen komponentinstansene slutter å eksistere i React-treet, og det fjerner ikke kostnaden ved JavaScript-en som trengs for å opprette dem. For enkle, hovedsakelig statiske innholdsblokker (kommentarer, avsnitt i en lang artikkel, tabellrader uten interaktivitet) er dette uvesentlig – den reelle kostnaden lå uansett i layout/paint, ikke i komponentlogikken. For lister satt sammen av tunge, interaktive komponenter – hver med sin egen state, abonnementer, kostbar rendering – er reell virtualisering, som fjerner ubrukte instanser fra React-treet fullstendig, fortsatt ofte den eneste måten å opprettholde jevnhet på.
Fallgruver og gode praksiser
contain-intrinsic-sizeer obligatorisk i praksis, ikke valgfritt. Å utelate det fører til hoppende scrolling og feil dokumenthøyde ved hver overgang for et element inn i og ut av hoppet-over-modus.content-visibility: hiddener ikke det samme somauto.hiddenhopper over rendring alltid, uansett posisjon på skjermen, men i motsetning tildisplay: nonebevarer den den interne tilstanden til den beregnede layouten i en cache – å vise elementet igjen (f.eks. ved fanebytte) er billigere enn etterdisplay: none, fordi nettleseren ikke regner ut alt fra bunnen av. Et element medcontent-visibility: hiddenfjernes samtidig korrekt fra tilgjengelighetstreet, akkurat somdisplay: none– dette er et trygt valg for faner og paneler som byttes ofte, ikke bare et ytelsestriks.- Det er ikke en erstatning for virtualisering ved lister med tunge komponenter. Behandle det som det første, billigere verktøyet for enkle, hovedsakelig statiske lister – grip til reell virtualisering først når profileringsverktøyet faktisk viser en kostnad på JS/React-siden, ikke bare i layouten.
- Sjekk støtte før du stoler fullt ut på «hidden but matchable». Selve
content-visibility: autohar i dag bred støtte i alle store motorer, men oppførselen med «midlertidig avsløring» ved Ctrl+F er ofte mest gjennomarbeidet i Chromium – behandle det som en hyggelig bonus, ikke som en garanti du baserer et tilgjengelighetskrav på.
Oppsummering
Virtualisering og content-visibility løser ved første øyekast det samme problemet – en lang liste som rendrer for sakte – men de gjør det på helt forskjellige nivåer. Virtualisering fjerner elementer fra DOM-en og fra React-treet fullstendig, og betaler for det med tap av Ctrl+F, utskrift og deler av tilgjengelighetsnavigasjonen. content-visibility lar DOM-en være komplett og ber bare nettleseren hoppe over det kostbare rendringsarbeidet for det som akkurat er usynlig – til gjengjeld får du en mindre aggressiv optimalisering, men gratis, uten å ødelegge noe nettleseren uansett allerede kunne gjøre med et komplett dokument. For de fleste lange, noenlunde enkle lister er dette riktig førstevalg – virtualisering holdes i reserve for tilfellene der dette reelt sett ikke er nok.