content-visibility: auto – amit a felhasználó nem lát, azt a böngésző meg sem rajzolja le

A content-visibility CSS tulajdonság auto értéke feltételesen kapcsolja ki a renderelést az off-screen tartalmakon. Megnézzük, hogyan működik a contain-intrinsic-size, a contentvisibilityautostatechange esemény, és mikor éri meg használni.

Ha valaha profilozóval néztél végig egy hosszú, sok szekcióból álló oldalt (kommentlista, termékkatalógus, dokumentáció), valószínűleg feltűnt, hogy a böngésző az első renderelésnél a teljes DOM-ra lefuttatja a style-t, a layoutot és a paintet – attól függetlenül, hogy a tartalom nagy része a görgetősáv alján van, amit a felhasználó épp nem lát. A content-visibility CSS tulajdonság pontosan ezt a felesleges munkát engedi kihagyni, és az auto értéke a legérdekesebb: nem egyszerűen elrejt valamit, hanem feltételesen kapcsolja ki a renderelést.

Diagram egy böngészőablakról: a látható területen kívüli szakaszok szaggatott kerettel, kihagyott tartalomként jelennek meg, a látható területen belüliek pedig teljesen renderelve vannak, jelezve a content-visibility: auto működését

Mit csinál valójában a content-visibility

A tulajdonságnak három értéke van:

  • visible – az alapértelmezett, nincs hatása.
  • hidden – a tartalom renderelése teljesen leáll, függetlenül a pozíciójától. Ez gyakorlatilag olyan, mint a display: none, azzal a különbséggel, hogy a böngésző megőrzi a renderelési állapotot, így ha vissza kell kapcsolni, nem kell nulláról újraépíteni. A kihagyott tartalom emiatt sem a keresésnél (Ctrl+F), sem a fókuszsorrendben, sem a képernyőolvasóban nem érhető el.
  • auto – ez a lényeg. A böngésző automatikusan alkalmaz layout-, style- és paint containmentet, és amíg az elem nem releváns a felhasználó számára (jellemzően off-screen van), addig kihagyja a layoutot, a festést és a hit-testinget is. Amint az elem közel kerül a viewporthoz, a normál renderelés visszaáll.

A kritikus különbség hidden és auto között az elérhetőség: az auto alatt lévő, épp kihagyott tartalom megmarad a hozzáférhetőségi fában, a Ctrl+F megtalálja, és tabbal be lehet fókuszálni – tehát nem „láthatatlan” a segédtechnológiák számára, csak a rendereléskor nem kap munkát a böngésző motorja. Ez a részlet a MDN dokumentációjában van pontosan leírva, és sok fejlesztő ezen szokott meglepődni: content-visibility: hidden és visibility: hidden közel sem ugyanaz.

A contain-intrinsic-size nélkül összeesik minden

Ha egy elemre rárakod a content-visibility: auto-t, és a böngésző kihagyja a layoutját, valahogy mégis tudnia kell, mekkora helyet foglaljon el az oldal többi részének elrendezésénél – különben minden off-screen szekció összeesne 0 magasságúra, ami erős ugrálást okozna a görgetősávban és görgetés közben. Erre való a contain-intrinsic-size:

.section {
  content-visibility: auto;
  contain-intrinsic-size: auto 500px;
}

Az auto 500px szintaxis két dolgot jelent egyszerre: az auto kulcsszó bekapcsolja az úgynevezett „remembered size” viselkedést – amikor a szekció ténylegesen renderelődött, a böngésző megjegyzi a valódi magasságát, és később, amikor újra kikerül a látótérből, ezt a megjegyzett méretet használja helyfoglalásnak, nem az 500px-et. Az 500px csak a fallback érték arra az esetre, ha még sosem renderelődött (pl. az első betöltéskor lévő off-screen tartalomnál). Ez a mechanizmus praktikusan azt jelenti, hogy nem kell fixen kitalálnod minden kártya vagy szekció magasságát – a böngésző saját magától tanulja meg.

Mikor éri meg, és mekkora a nyereség

A web.dev bemutatója egy hosszú, kártyás blog layoutot mért: a content-visibility: auto bevezetésével a kezdeti renderelési idő 232 ms-ról 30 ms-ra csökkent a tesztoldalon. Nem minden oldalon lesz ekkora a nyereség, de a minta világos: minél nagyobb és minél inkább szekciókra tagolt a DOM, annál többet nyersz azzal, hogy a böngészőnek nem kell azonnal kiszámolnia mindent, ami épp nem látszik.

Tipikus jó jelöltek: hosszú kommentszekciók, sok kártyából álló galériák, dokumentáció-oldalak sok szakasszal, hosszú termék- vagy cikklisták. Ezekben gyakran natívan megoldja azt, amiért korábban külön virtualizációs könyvtárat kellett volna bevezetni – jóval kevesebb kóddal.

A contentvisibilityautostatechange esemény

Van egy másik, kevésbé ismert darab is a képben: a böngésző eseményt is küld, amikor egy content-visibility: auto elem renderelése elindul vagy leáll. Ez azért hasznos, mert a CSS önmagában csak a layoutot/festést hagyja ki – de ha az elemben fut egy setInterval, egy canvas-animáció vagy egy videó, azt a CSS nem állítja le neked. Erre való a contentvisibilityautostatechange esemény:

const cards = document.querySelectorAll('.card');

cards.forEach((card) => {
  card.addEventListener('contentvisibilityautostatechange', (event) => {
    if (event.skipped) {
      // az elem kikerült a látható területből, leáll a munka
      clearInterval(card._timer);
    } else {
      // az elem újra látható, folytatódhat a munka
      card._timer = setInterval(() => {
        card.querySelector('.tick').textContent = Date.now();
      }, 1000);
    }
  });
});

Az event.skipped értéke true, amikor az elem éppen kimarad a renderelésből, és false, amikor visszakerül. Így egy hosszú oldalon lévő, sok kis widgetből (élő számláló, canvas-alapú grafikon, animáció) álló listánál pontosan azt tudod leállítani a JS oldalán is, amit a böngésző már amúgy sem rajzol ki – ami CPU-t és akkumulátort spórol.

A korlátok, amikről érdemes tudni

  • Ha a JS-ből olyan DOM API-t hívsz meg egy kihagyott elemen, ami kényszeríti a layoutot (pl. getBoundingClientRect(), focus()), a böngésző azonnal, szinkron módon kénytelen renderelni az adott elemet – ez pont azt a munkát végzi el, amit a content-visibility egyébként megspórolna, ezért érdemes kerülni az ilyen hívásokat tömegesen (pl. egy scroll eventben végigmenni az összes kártyán és lekérdezni a pozícióját).
  • A contain-intrinsic-size nélkül a layout ugrálni fog – ez szinte kötelező páros.
  • Nyomtatásnál és az oldalon belüli keresésnél auto esetén a tartalom elérhető marad, de érdemes külön tesztelni, ha ez kritikus a projektben.
  • Ez nem helyettesíti a valódi lista-virtualizációt extrém esetekben (több tízezer elem), mert a DOM-csomópontok maguk így is megmaradnak – csak a renderelési munkájuk marad el. Nagyon nagy listáknál a DOM-csomópontok száma önmagában is számít (memóriahasználat, kereső funkciók sebessége).

Böngészőtámogatás

A tulajdonság mára minden nagy motorban elérhető: Chrome és Edge 85 óta, Firefox 125 óta (a 109–124-es verziókban még flag mögött volt), Safari pedig 18.0-tól támogatja – ez volt az utolsó darab, ami miatt korábban sok csapat csak progresszív enhancementként mert hozzányúlni. A pontos, frissülő táblázatot a caniuse oldalán érdemes ellenőrizni.

Összegzés

A content-visibility: auto és a contain-intrinsic-size párosa gyakorlatilag ingyen kapott renderelés-optimalizáció minden hosszú, szekciókra bontható oldalhoz – nem kell hozzá JS-keretrendszer vagy saját, Intersection Observer-alapú virtualizáció, elég két CSS sor. A contentvisibilityautostatechange esemény pedig azoknak kell, akiknek az elemben tényleges futó munka is van, nem csak statikus tartalom. Ha legközelebb egy hosszú listát vagy dokumentumoldalt optimalizálsz, ez az első dolog, amit érdemes kipróbálni – jóval a saját virtualizációs logika megírása előtt.

Források

Leave a Reply

Az e-mail címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük