A Ctrl+F kinyithatja az accordionodat: hidden=”until-found” a gyakorlatban

A hidden="until-found" kereshetővé teszi a becsukott tartalmat, a beforematch pedig szinkronban tartja a komponenst. A dobozmodell és egy régi CSS-reset viszont könnyen elrontja.

Megnyomod a Ctrl+F-et egy hosszú dokumentációban, beírod a hibakódot, a böngésző pedig közli, hogy nincs találat. A szöveg ott van, csak egy becsukott, JavaScripttel hajtogatott panelben. Technikailag korrekt felületet építettünk; használni történetesen rossz.

A hidden="until-found" erre ad natív megoldást: a tartalom csukva marad, de a böngésző oldalon belüli keresője és a fragmentnavigáció megtalálhatja, majd felfedheti. A hozzá tartozó beforematch eseménnyel a saját komponensállapotunkat is rendbe rakhatjuk. Az API 2025 végétől Baseline 2025, vagyis már nem kizárólag egy Chromium-demó kedvéért létezik.

A böngésző keresője egy összecsukott tartalmi panelt nyit ki

Nem egy udvariasabb display: none

A sima hidden állapotot a böngésző tipikusan display: none-nal valósítja meg. Az elem nem renderelődik, a Ctrl+F nem lát bele. A HTML szabvány külön állapotként definiálja a hidden="until-found" értéket: a belső tartalom nincs kirajzolva, de kereshető marad.

Találat vagy #fragment célpont esetén a böngésző először beforematch eseményt küld a rejtő elemre, utána eltávolítja a hidden attribútumot, végül odagörget. Nem kell a keresőmező billentyűit lesni – amúgy sem kapsz hozzá normális API-t –, a böngésző elvégzi a kellemetlen részt.

<section class="faq">
  <h2>
    <button id="cache-button" type="button"
      aria-expanded="false" aria-controls="cache-answer">
      Miért régi a válasz a cache-ben?
    </button>
  </h2>

  <div id="cache-answer" hidden="until-found">
    <div class="faq__body">
      A stale-while-revalidate a háttérben frissít.
    </div>
  </div>
</section>

A belső wrapper nem dísznek van. A hidden until found állapot jellemzően content-visibility: hidden-t használ. Emiatt a külső elemnek továbbra is van doboza: a margin, padding, keret és háttér látszhat akkor is, amikor a tartalom csukva van. Ha a keretet a .faq__body elemre tesszük, nem marad egy megmagyarázhatatlan üres téglalap a lapon.

A JavaScript feladata: ne hazudjon az állapot

A böngésző az attribútumot el tudja távolítani, de a gomb aria-expanded értékéről és a saját nyilacskánk CSS-osztályáról semmit sem tud. Ha ezeket nem szinkronizáljuk, a panel nyitva lesz, miközben a gomb továbbra is azt állítja, hogy csukva. Apró részlet, egészen addig, amíg valaki képernyőolvasóval próbálja használni.

const button = document.querySelector("#cache-button");
const panel = document.querySelector("#cache-answer");

function setExpanded(expanded) {
  button.setAttribute("aria-expanded", String(expanded));

  if (expanded) panel.removeAttribute("hidden");
  else panel.setAttribute("hidden", "until-found");
}

button.addEventListener("click", () => {
  setExpanded(panel.hasAttribute("hidden"));
});

panel.addEventListener("beforematch", () => {
  setExpanded(true);
});

A beforematch közvetlenül a felfedés előtt fut. A handlerben ezért nemcsak az ARIA-állapotot, hanem a komponens memóriában tartott állapotát is frissíteni kell. Reactben, Vue-ban vagy saját state machine-ban ugyanez a szabály: a DOM attribútumának eltűnése ne legyen a komponens háta mögött történt puccs.

A felfedés ráadásul egyirányú. Amikor a felhasználó bezárja a keresőt vagy továbblép egy másik találatra, a böngésző nem csukja vissza a panelt. Ez jó alapértelmezés: különben az éppen megtalált tartalom úgy tűnne el, mint a túl gyorsan bezáródó hover menü. A visszacsukás maradjon explicit felhasználói művelet.

Fragmentlinknél ugyanez a folyamat fut le. Egy /sugo#cache-invalidation URL ezért közvetlenül célozhat a rejtett blokk egyik leszármazottjára; a böngésző felfedi az útban lévő until-found őst, majd görget. Szerveroldali renderelésnél ez különösen hasznos, mert a mélylink már az első HTML-ben működik, nem kell egy kliensoldali routernek találgatnia, melyik accordion alatt lakik a célpont.

A reset CSS, amely csendben elrontja

Az igazán „Na, ezt nem tudtam.” rész a layout containment. A felfedéshez az elemnek layout containment alatt kell állnia; display: none, display: contents vagy display: inline mellett a kereső nem tudja kinyitni. A régi resetekben gyakori szabály tehát konkrétan lenullázza az egész funkciót:

/* Ezt ne alkalmazd az until-found állapotra. */
[hidden] { display: none !important; }

/* Ha a resethez ragaszkodsz, legalább szűkítsd. */
[hidden]:not([hidden="until-found"]) {
  display: none !important;
}

A Chrome fejlesztői leírása ugyanerre a dobozmodell-csapdára figyelmeztet. Érdemes a DevToolsban nemcsak azt nézni, hogy ott van-e az attribútum, hanem a computed display és content-visibility értékét is.

Van még egy klasszikus HTML-csapda: a hidden enumerált attribútum, de az érvénytelen érték a sima rejtett állapotba esik vissza. Vagyis a hidden="false" nem hamisat jelent, hanem elrejti az elemet. JavaScriptből a szándékot érdemes egyértelműen removeAttribute("hidden")-nel, illetve setAttribute("hidden", "until-found")-dal kifejezni; így egy kódreview-n sem kell a HTML attribútumlogikájáról rögtönzött filozófiai vitát nyitni.

Mikor jó, és mikor csak újabb varázslat?

Hosszú dokumentáció, FAQ, changelog és beállítási felület saját accordion komponensénél használnám productionben. Progresszív fejlesztésnek is korrekt: nem támogató böngészőben az ismeretlen érték sima rejtett állapotként viselkedik, ezért szükség esetén 'onbeforematch' in HTMLElement.prototype ellenőrzéssel ki lehet nyitni az összes panelt.

Ha elég a natív <details>, előbb azt választanám: a szabvány a zárt details tartalmát is bevonja az oldalkeresésbe, és találatkor kinyitja. Ha a tartalmat csak nyitáskor kéred le a szerverről, az until-found nem segít, mert a böngésző nem kereshet olyan szövegben, amely még nincs a DOM-ban. Titkot pedig végképp ne ezzel rejts: a rejtett leszármazottak továbbra is aktívak lehetnek, a HTML pedig ugyanúgy letöltődött.

Az akadálymentességet sem oldja meg önmagában. A csukott tartalom nincs prezentálva a felhasználónak, így kell egy elérhető nevű, billentyűzettel működő gomb és helyes aria-expanded/aria-controls kapcsolat. Az until-found nem az inert rokonszenvesebb neve, és nem engedély arra, hogy egy kattintható div-ből újabb házi vezérlőt faragjunk.

A támogatás ma már használható, de nem hibátlan történelem: a WebKitnél dokumentált görgetési probléma emlékeztet rá, hogy a „megtalálja” és a „jó helyre is viszi” nem mindig ugyanaz. Emiatt a kézi nyitógombot megtartanám, és legalább Safari, Firefox, Chrome alatt végigpróbálnám a Ctrl+F-et és a közvetlen fragmentlinket. Kevés kódért cserébe visszakapunk egy alapvető böngészőfunkciót. Ez ritka üzlet; általában fordítva szokott történni.

Források

Leave a Reply

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