Die scenario is byna altyd dieselfde, wat 'n datatabel binne 'n blaaibare houer is. Elke ry het 'n aksiekieslys, 'n klein aftreklys met 'n paar opsies, soos Redigeer, Dupliseer en Skrap. Jy bou dit, dit lyk asof dit perfek in isolasie werk, en dan sit iemand dit binne daardie scrollbare div en dinge val uitmekaar. Ek het hierdie presiese fout in drie verskillende kodebasisse gesien: die houer, die stapel en die raamwerk, almal verskillend. Die fout is egter heeltemal identies. Die aftreklys word by die houer se rand geknip. Of dit verskyn agter inhoud wat logieserwys daaronder behoort te wees. Of dit werk goed totdat die gebruiker blaai, en dan dryf dit. Jy reik na z-indeks: 9999. Soms help dit, maar ander kere doen dit absoluut niks. Daardie inkonsekwentheid is die eerste leidraad dat iets dieper gebeur. Die rede waarom dit steeds terugkom, is dat drie afsonderlike blaaierstelsels betrokke is, en die meeste ontwikkelaars verstaan elkeen op sy eie, maar dink nooit aan wat gebeur wanneer al drie bots nie: oorloop, stapelkontekste en blokke bevat.
Sodra jy verstaan hoe al drie interaksie het, hou die mislukkingsmodusse op om lukraak te voel. Trouens, hulle word voorspelbaar. Die drie dinge wat dit eintlik veroorsaak Kom ons kyk in detail na elkeen van daardie items. Die oorloopprobleem Wanneer jy oorloop: versteek, oorloop: blaai, of oorloop: outomaties op 'n element stel, sal die blaaier enigiets knip wat buite sy grense strek, insluitend absoluut geposisioneerde afstammelinge. .scroll-houer { oorloop: outomaties; hoogte: 300px; /* Dit sal die aftreklys knip, punt */ }
.dropdown { posisie: absoluut; /* Maak nie saak nie -- steeds geknip deur .scroll-houer */ }
Dit het my verras toe ek dit vir die eerste keer raakloop. Ek het posisie ingeneem: absoluut sou 'n element 'n houer se knip laat ontsnap. Dit doen nie. In die praktyk beteken dit dat 'n absoluut geposisioneerde spyskaart afgesny kan word deur enige voorouer wat 'n nie-sigbare oorloopwaarde het, selfs al is daardie voorouer nie die spyskaart se inhoudblok nie. Knip en posisionering is aparte stelsels. Hulle bots toevallig op maniere wat heeltemal lukraak lyk totdat jy albei verstaan.
Hier is 'n React-voorbeeld met behulp van createPortal:
invoer { createPortal } vanaf 'react-dom'; invoer { useState, useEffect, useRef } vanaf 'react';
function Dropdown ({ anchorRef, isOpen, children }) { const [posisie, stelPosisie] = useState({ top: 0, links: 0 });
useEffect(() => { if (isOop && anchorRef.current) { const rect = anchorRef.current.getBoundingClientRect(); stelposisie({ bo: rect.bottom + window.scrollY, links: rect.left + window.scrollX, }); } }, [isOpen, anchorRef]);
if (!isOop) gee nul terug;
gee terug createPortal(
En natuurlik kan ons nie toeganklikheid ignoreer nie. Vaste elemente wat oor inhoud verskyn, moet steeds sleutelbordbereikbaar wees. As die fokusvolgorde nie natuurlik na die vaste aftreklys beweeg nie, sal jy dit met kode moet bestuur. Dit is ook die moeite werd om seker te maak dat dit nie oor ander interaktiewe inhoud sit met geen manier om dit te verwerp nie. Daai een byt jou in sleutelbordtoetsing. CSS-ankerposisionering: waarheen ek dink dit is op pad CSS-ankerposisionering is die rigting waarin ek tans die meeste belangstel. Ek was nie seker hoeveel van die spesifikasie eintlik bruikbaar was toe ek die eerste keer daarna gekyk het nie. Dit laat jou die verhouding tussen 'n aftreklys en sy sneller direk in CSS verklaar, en die blaaier hanteer die koördinate. .trigger { ankernaam: --my-sneller; }
.dropdown-menu { posisie: absoluut; posisie-anker: --my-sneller; bo: anker(onder); links: anker(links); posisie-probeer-terugvalle: flip-block, flip-inline; }
Die posisie-probeer-terugval-eienskap is wat dit die moeite werd maak om oor 'n handmatige berekening te gebruik. Die blaaier probeer alternatiewe plasings voordat hy tou opgooi, so 'n aftreklys aan die onderkant van die viewport blaai outomaties opwaarts in plaas daarvan om afgesny te word. Blaaierondersteuning is solied in Chromium-gebaseerde blaaiers en groei in Safari. Firefox benodig 'n polyfill. Die @oddbird/css-anchor-positioning-pakket dek die kernspesifikasie. Ek het uitlegrandgevalle daarmee getref wat terugval vereis het wat ek nie verwag het nie, so behandel dit as 'n progressiewe verbetering of koppel dit met 'nJavaScript-terugval vir Firefox. Kortom, belowend maar nog nie universeel nie. Toets in jou teikenblaaiers. En wat toeganklikheid betref, sê die verklaring van 'n visuele verhouding in CSS niks vir die toeganklikheidsboom nie. aria-kontroles, aria-uitgebreide, aria-haspopup - daardie deel is nog steeds op jou. Soms is die oplossing net om die element te skuif Voordat ek na 'n portaal gryp of koördinaatberekeninge maak, vra ek altyd eers een vraag: Moet hierdie aftreklys eintlik in die rolhouer woon? As dit nie die geval is nie, skakel die opmaak na 'n hoërvlak-omhulsel die probleem heeltemal uit, met geen JavaScript en geen koördinaatberekeninge nie. Dit is nie altyd moontlik nie. As die knoppie en aftreklys in dieselfde komponent ingekapsuleer is, beteken dit om die hele API te heroorweeg om die een sonder die ander te skuif. Maar wanneer jy dit kan doen, is daar niks om te ontfout nie. Die probleem bestaan net nie. Wat moderne CSS steeds nie oplos nie CSS het 'n lang pad hier gestap, maar daar is steeds plekke waar dit jou in die steek laat. Die posisie: vaste en transformeer kwessies is steeds daar. Dit is doelbewus in die spesifikasie, wat beteken dat daar geen CSS-oplossing bestaan nie. As jy 'n animasiebiblioteek gebruik wat jou uitleg in 'n getransformeerde element toevou, het jy weer portale of ankerposisionering nodig. CSS-ankerposisionering is belowend, maar nuut. Soos vroeër genoem, het Firefox steeds 'n polyfill nodig op die oomblik dat ek hierdie skryf. Ek het uitlegrandgevalle daarmee getref wat terugslae vereis het wat ek nie verwag het nie. As jy vandag konsekwente gedrag oor alle blaaiers heen nodig het, soek jy steeds JavaScript vir die moeilike dele. Die toevoeging waarvoor ek eintlik my werkvloei verander het, is die HTML Popover API, nou beskikbaar in alle moderne blaaiers. Elemente met die popover-kenmerk word in die blaaier se boonste laag vertoon, bo alles, sonder dat JavaScript-posisionering nodig is.
Ontsnappingshantering, wegwys-op-klik-buite, en soliede toeganklikheid semantiek kom gratis vir dinge soos gereedskapwenke, openbaarmaking legstukke en eenvoudige oorlegsels. Dit is die eerste instrument wat ek nou bereik. Dit gesê, dit los nie posisionering op nie. Dit los lae op. Jy het steeds ankerposisionering of JavaScript nodig om 'n popover by sy sneller in lyn te bring. Die Popover API hanteer die lae. Ankerposisionering hanteer die plasing. As dit saam gebruik word, dek hulle die meeste van wat jy voorheen na 'n biblioteek sou bereik het om te doen. 'n Besluitgids vir jou situasie Nadat ek op die harde manier deur dit alles gegaan het, is dit hoe ek nou eintlik oor die keuse dink.
Gebruik 'n portaal. Ek sal dit gebruik wanneer die sneller diep in geneste rolhouers woon. Ek het hierdie patroon vir tafelaksie-spyskaarte gebruik en dit met fokusherstel en toeganklikheidkontrole gekoppel. Dit is die mees betroubare opsie, maar begroot tyd vir die ekstra bedrading. Gebruik vaste posisionering. Dit is vir wanneer jy in vanielje JavaScript of 'n liggewig raamwerk is en kan verifieer dat geen voorouer transformasies of filters toepas nie. Dit is eenvoudig om op te stel en maklik om te ontfout, solank daardie een beperking geld. Gebruik CSS Anchor Positioning. Reach hiervoor wanneer jou blaaierondersteuning dit toelaat. As Firefox ondersteuning vereis word, koppel dit met die @oddbird polyfill. Dit is waarheen die platform uiteindelik op pad is en uiteindelik jou go-to-benadering sal word. Herstruktureer die DOM. Gebruik dit wanneer die argitektuur dit toelaat, en jy wil geen runtime kompleksiteit hê nie. Ek glo dit is waarskynlik die mees onderskatte opsie. Kombineer patrone. Doen dit wanneer jy ankerposisionering as jou primêre benadering wil hê, gepaard met 'n JavaScript-terugval vir nie-ondersteunde blaaiers. Of 'n portaal vir DOM-plasing gepaard met getBoundingClientRect() vir koördinaatakkuraatheid.
Gevolgtrekking Ek het hierdie fout gebruik as 'n eenmalige probleem - iets om te pleister en van aan te gaan. Maar sodra ek lank genoeg daarmee gesit het om al drie betrokke stelsels te verstaan - oorloopknip, stapelkontekste en blokke bevat - het dit opgehou om lukraak te voel. Ek kon na 'n gebroke aftreklys kyk en dadelik opspoor watter voorouer verantwoordelik was. Daardie verskuiwing in hoe ek die DOM lees was die regte wegneemete. Daar is geen enkele regte antwoord nie. Waarna ek gegryp het, het afgehang van wat ek in die kodebasis kon beheer: portale wanneer die voorouerboom onvoorspelbaar was; vaste posisionering wanneer dit skoon en eenvoudig was; beweeg die element wanneer niks my keer nie; en ankerposisionering nou,waar ek kan. Wat jy ook al op die ou end kies, moenie toeganklikheid as die laaste stap behandel nie. In my ervaring is dit presies wanneer dit oorgeslaan word. Die ARIA-verhoudings, die fokusbestuur, die sleutelbordgedrag - dit is nie poets nie. Hulle is deel van wat die ding eintlik laat werk. Kyk na die volledige bronkode in my GitHub-repo. Verdere lees Dit is die verwysings waarna ek heeltyd teruggekom het terwyl ek hierdeur gewerk het:
Die stapelkonteks (MDN) “CSS-ankerposisioneringsgids”, Juan Diego Rodriguez "Aan die gang met die Popover API", Godstime Aburu Swaai UI (floating-ui.com) CSS-oorloop (MDN)