Ang senaryo ay halos palaging pareho, na isang talahanayan ng data sa loob ng isang na-scroll na lalagyan. Ang bawat row ay may action menu, isang maliit na dropdown na may ilang opsyon, tulad ng Edit, Duplicate, at Delete. Binubuo mo ito, tila gumagana nang perpekto sa paghihiwalay, at pagkatapos ay may naglalagay nito sa loob ng na-scroll na div na iyon at nagkawatak-watak ang mga bagay. Nakita ko ang eksaktong bug na ito sa tatlong magkakaibang codebase: ang lalagyan, ang stack, at ang framework, lahat ay magkakaiba. Ang bug, bagaman, ay ganap na magkapareho. Ang dropdown ay mapuputol sa gilid ng lalagyan. O nagpapakita ito sa likod ng nilalaman na dapat ay lohikal na nasa ibaba nito. O ito ay gumagana nang maayos hanggang sa mag-scroll ang user, at pagkatapos ay mag-drift ito. Naabot mo ang z-index: 9999. Minsan nakakatulong ito, ngunit sa ibang pagkakataon ay talagang wala itong ginagawa. Ang hindi pagkakapare-pareho ang unang palatandaan na may mas malalim na nangyayari. Ang dahilan kung bakit patuloy itong bumabalik ay ang tatlong magkahiwalay na mga browser system ay kasangkot, at karamihan sa mga developer ay nauunawaan ang bawat isa sa sarili nitong ngunit hindi kailanman iniisip kung ano ang mangyayari kapag ang tatlo ay nagbanggaan: overflow, stacking contexts, at naglalaman ng mga block.
Kapag naunawaan mo kung paano nakikipag-ugnayan ang tatlo, ang mga mode ng pagkabigo ay hihinto sa pagiging random. Sa katunayan, nagiging predictable sila. Ang Tatlong Bagay na Nagdudulot Nito Tingnan natin ang bawat isa sa mga item na iyon nang detalyado. Ang Problema sa Pag-apaw Kapag nagtakda ka ng overflow: hidden, overflow: scroll, o overflow: auto sa isang elemento, i-clip ng browser ang anumang bagay na lalampas sa mga hangganan nito, kabilang ang ganap na nakaposisyon na mga inapo. .scroll-container { overflow: auto; taas: 300px; /* I-clip nito ang dropdown, full stop */ }
.dropdown { posisyon: ganap; /* Hindi mahalaga -- pinutol pa rin ng .scroll-container */ }
Nagulat ako sa unang pagkakataon na nasagasaan ko ito. I'd assumed position: ang absolute ay hahayaan ang isang elemento na makatakas sa clipping ng container. hindi ito. Sa pagsasagawa, nangangahulugan iyon na ang isang ganap na nakaposisyon na menu ay maaaring putulin ng sinumang ninuno na may hindi nakikitang halaga ng overflow, kahit na ang ninuno na iyon ay hindi naglalaman ng bloke ng menu. Ang pag-clip at pagpoposisyon ay magkahiwalay na mga sistema. Nagkataon lang na nagbanggaan sila sa mga paraan na mukhang random hanggang sa maunawaan mo pareho.
Narito ang isang halimbawa ng React gamit ang createPortal:
import { createPortal } mula sa 'react-dom'; mag-import ng { useState, useEffect, useRef } mula sa 'react';
function na Dropdown({ anchorRef, isOpen, mga bata }) { const [posisyon, setPosition] = useState({top: 0, left: 0 });
useEffect(() => { if (isOpen && anchorRef.current) { const rect = anchorRef.current.getBoundingClientRect(); setPosition({ itaas: rect.bottom + window.scrollY, kaliwa: rect.left + window.scrollX, }); } }, [isOpen, anchorRef]);
kung (!isOpen) return null;
ibalik ang createPortal(
At, siyempre, hindi namin maaaring balewalain ang accessibility. Ang mga nakapirming elemento na lumalabas sa nilalaman ay dapat pa ring maabot ng keyboard. Kung ang pagkakasunud-sunod ng focus ay hindi natural na lumipat sa nakapirming dropdown, kakailanganin mong pamahalaan ito gamit ang code. Ito rin ay nagkakahalaga ng pagsuri na hindi ito nauukol sa iba pang interactive na nilalaman na walang paraan upang i-dismiss ito. Kinakagat ka niyan sa pagsubok sa keyboard. CSS Anchor Positioning: Kung Saan Sa Palagay Ko Ito ang Patungo Ang CSS Anchor Positioning ay ang direksyon na pinakainteresado ko ngayon. Hindi ako sigurado kung gaano talaga magagamit ang spec noong una kong tiningnan ito. Hinahayaan ka nitong ipahayag ang kaugnayan sa pagitan ng dropdown at trigger nito nang direkta sa CSS, at pinangangasiwaan ng browser ang mga coordinate. .trigger { anchor-name: --my-trigger; }
.dropdown-menu { posisyon: ganap; position-anchor: --my-trigger; itaas: anchor(ibaba); kaliwa: anchor(kaliwa); position-try-fallbacks: flip-block, flip-inline; }
Ang property na position-try-fallbacks ang dahilan kung bakit ito nagkakahalaga ng paggamit sa manu-manong pagkalkula. Sinusubukan ng browser ang mga alternatibong placement bago sumuko, kaya ang isang dropdown sa ibaba ng viewport ay awtomatikong pumipihit paitaas sa halip na maputol. Solid ang suporta sa browser sa mga browser na nakabatay sa Chromium at lumalaki sa Safari. Kailangan ng Firefox ng polyfill. Sinasaklaw ng @oddbird/css-anchor-positioning package ang core spec. Naabot ko ang mga kaso sa gilid ng layout na nangangailangan ng mga fallback na hindi ko inaasahan, kaya ituring ito bilang isang progresibong pagpapahusay o ipares ito sa isangJavaScript fallback para sa Firefox. In short, promising pero hindi pa unibersal. Subukan sa iyong mga target na browser. At kung tungkol sa accessibility, ang pagdedeklara ng visual na relasyon sa CSS ay hindi nagsasabi sa puno ng accessibility ng anuman. aria-controls, aria-expanded, aria-haspopup — nasa iyo pa rin ang bahaging iyon. Minsan Ang Pag-aayos ay Paggalaw Lang Sa Elemento Bago maabot ang isang portal o gumawa ng mga kalkulasyon ng coordinate, palagi akong nagtatanong ng isang tanong muna: Kailangan ba talagang mabuhay ang dropdown na ito sa loob ng scroll container? Kung hindi, ang paglipat ng markup sa isang mas mataas na antas ng wrapper ay ganap na nag-aalis ng problema, nang walang JavaScript at walang mga kalkulasyon ng coordinate. Ito ay hindi laging posible. Kung ang button at dropdown ay naka-encapsulate sa parehong bahagi, ang paglipat ng isa nang wala ang isa ay nangangahulugan ng muling pag-iisip sa buong API. Ngunit kapag nagawa mo na ito, wala nang dapat i-debug. Ang problema ay wala lang. Ano ang Hindi Pa Nalulutas ng Makabagong CSS Malayo na ang narating ng CSS dito, ngunit mayroon pa ring mga lugar na hinahayaan ka nito. Ang posisyon: nariyan pa rin ang mga isyu sa naayos at pagbabago. Sinadya itong nasa spec, na nangangahulugang walang CSS workaround na umiiral. Kung gumagamit ka ng animation library na bumabalot sa iyong layout sa isang binagong elemento, babalik ka sa pangangailangan ng mga portal o pagpoposisyon ng anchor. Ang CSS Anchor Positioning ay promising, ngunit bago. Gaya ng nabanggit kanina, kailangan pa rin ng Firefox ng polyfill sa oras na isinusulat ko ito. Naabot ko ang mga kaso sa gilid ng layout na nangangailangan ng mga fallback na hindi ko inaasahan. Kung kailangan mo ng pare-parehong pag-uugali sa lahat ng browser ngayon, inaabot mo pa rin ang JavaScript para sa mga nakakalito na bahagi. Ang karagdagan na talagang binago ko sa aking workflow ay ang HTML Popover API, na available na ngayon sa lahat ng modernong browser. Nagre-render ang mga elementong may attribute na popover sa tuktok na layer ng browser, higit sa lahat, nang hindi kailangan ng pagpoposisyon ng JavaScript.
Ang paghawak sa escape, dismiss-on-click-outside, at solidong accessibility semantics ay libre para sa mga bagay tulad ng mga tooltip, mga widget sa pagsisiwalat, at mga simpleng overlay. Ito ang unang tool na naabot ko sa ngayon. Iyon ay sinabi, hindi nito nalulutas ang pagpoposisyon. Nilulutas nito ang layering. Kailangan mo pa rin ng anchor positioning o JavaScript upang ihanay ang isang popover sa trigger nito. Pinangangasiwaan ng Popover API ang layering. Ang pagpoposisyon ng anchor ang humahawak sa pagkakalagay. Kapag ginamit nang magkasama, sinasaklaw nila ang karamihan sa kung ano ang dati mong naabot para sa isang library na gawin. Isang Gabay sa Pagpapasya Para sa Iyong Sitwasyon Matapos ang lahat ng ito sa mahirap na paraan, narito kung paano ko talaga iniisip ang tungkol sa pagpili ngayon.
Gumamit ng portal. Gagamitin ko ito kapag ang trigger ay nabubuhay nang malalim sa mga nested scroll container. Ginamit ko ang pattern na ito para sa mga menu ng pagkilos ng talahanayan at ipinares ito sa pagpapanumbalik ng focus at mga pagsusuri sa pagiging naa-access. Ito ang pinaka-maaasahang opsyon, ngunit oras ng badyet para sa dagdag na mga kable. Gumamit ng nakapirming pagpoposisyon. Ito ay para sa kapag ikaw ay nasa vanilla JavaScript o isang magaan na framework at maaaring ma-verify na walang ninuno na naglalapat ng mga pagbabago o filter. Ito ay simple upang i-set up at simpleng i-debug, hangga't ang isang hadlang ay humahawak. Gamitin ang CSS Anchor Positioning. Abutin ito kapag pinayagan ito ng iyong browser support. Kung kailangan ng suporta sa Firefox, ipares ito sa @oddbird polyfill. Ito ay kung saan ang platform sa huli ay patungo at sa kalaunan ay magiging iyong diskarte. I-restructure ang DOM. Gamitin ito kapag pinahihintulutan ito ng arkitektura, at gusto mo ng zero runtime complexity. Naniniwala ako na malamang na ito ang pinaka-underrated na opsyon. Pagsamahin ang mga pattern. Gawin ito kapag gusto mo ang anchor positioning bilang iyong pangunahing diskarte, na ipinares sa isang fallback ng JavaScript para sa mga hindi sinusuportahang browser. O isang portal para sa placement ng DOM na ipinares sa getBoundingClientRect() para sa katumpakan ng coordinate.
Konklusyon Tinatrato ko noon ang bug na ito bilang isang one-off na isyu — isang bagay na dapat i-patch at ipagpatuloy. Ngunit sa sandaling umupo ako kasama nito nang sapat na matagal upang maunawaan ang lahat ng tatlong system na kasangkot - overflow clipping, stacking konteksto, at naglalaman ng mga bloke - tumigil ito sa pakiramdam ng random. Nakatingin ako sa isang sirang dropdown at agad na natunton kung sinong ninuno ang may pananagutan. Ang pagbabagong iyon sa kung paano ko basahin ang DOM ay ang tunay na takeaway. Walang iisang tamang sagot. Ang naabot ko ay nakasalalay sa kung ano ang maaari kong kontrolin sa codebase: mga portal kapag ang puno ng ninuno ay hindi mahuhulaan; nakapirming pagpoposisyon kapag ito ay malinis at simple; paglipat ng elemento kapag walang pumipigil sa akin; at anchor positioning ngayon,kung saan ko kaya. Anuman ang pipiliin mo, huwag ituring ang pagiging naa-access bilang huling hakbang. Sa aking karanasan, iyon mismo ay kapag ito ay nalaktawan. Ang mga relasyon sa ARIA, ang pamamahala sa pagtutok, ang pag-uugali ng keyboard — hindi maganda ang mga iyon. Bahagi sila ng kung ano ang gumagawa ng bagay na talagang gumagana. Tingnan ang buong source code sa aking GitHub repo. Karagdagang Pagbasa Ito ang mga sanggunian na paulit-ulit kong binabalikan habang ginagawa ito:
Ang Stacking Context (MDN) “CSS Anchor Positioning Guide”, Juan Diego Rodriguez "Pagsisimula Sa Popover API", Godstime Aburu Lumulutang na UI (floating-ui.com) CSS Overflow (MDN)