場景幾乎總是相同的,即可捲動容器內的資料表。每行都有一個操作選單,一個帶有一些選項的小下拉式選單,例如編輯、複製和刪除。你建造它,它似乎可以完美地獨立工作,然後有人將它放入可滾動的 div 中,事情就崩潰了。我在三個不同的程式碼庫中看到了這個確切的錯誤:容器、堆疊和框架,全都不同。不過,這個錯誤是完全相同的。 下拉式選單被剪切在容器的邊緣。或者它出現在邏輯上應該位於其下方的內容後面。或者它工作得很好,直到用戶滾動,然後它就漂移了。 您可以使用 z-index: 9999。有時它會有所幫助,但有時它完全沒有任何作用。這種不一致是顯示更深層的事情正在發生的第一個線索。 它不斷出現的原因是涉及三個獨立的瀏覽器系統,大多數開發人員都單獨了解每個瀏覽器系統,但從未考慮過當這三個系統發生衝突時會發生什麼:溢出、堆疊上下文和包含區塊。

一旦您了解了這三者如何相互作用,故障模式就不再感覺是隨機的。事實上,它們變得可預測。 實際上導致這種情況的三件事 讓我們詳細看看每一項。 溢出問題 當您在元素上設定溢位:隱藏、溢位:捲動或溢位:自動時,瀏覽器將剪切超出其邊界的任何內容,包括絕對定位的後代。 .scroll-container { 溢出:自動; 高度:300px; /* 這將剪輯下拉選單,句號 */ }

.dropdown { 位置:絕對; /* 沒關係——仍然被 .scroll-container 剪切 */ }

當我第一次遇到它時,這讓我很驚訝。我假設position:absolute會讓元素逃脫容器的裁切。事實並非如此。 實際上,這意味著絕對定位的菜單可以被具有不可見溢出值的任何祖先切斷,即使該祖先不是菜單的包含區塊。剪切和定位是獨立的系統。它們只是碰巧以看起來完全隨機的方式發生碰撞,直到你理解兩者為止。

這是一個使用 createPortal 的 React 範例:

從'react-dom'導入{createPortal}; 從 'react' 導入 { useState, useEffect, useRef };

函數 Dropdown({anchorRef, isOpen, 兒童 }) { const [位置,setPosition] = useState({ 頂部:0,左側:0 });

使用效果(()=> { if (isOpen &&anchorRef.current) { const rect =anchorRef.current.getBoundingClientRect(); 設定位置({ 頂部:矩形.底部+視窗.scrollY, 左: rect.left + window.scrollX, }); } }, [isOpen,anchorRef]);

if (!isOpen) 回傳 null;

返回建立入口網站(

{孩子}
, 文件正文 ); }

當然,我們不能忽視可訪問性。出現在內容上方的固定元素必須仍可透過鍵盤存取。如果焦點順序沒有自然地移動到固定下拉清單中,您需要使用程式碼來管理它。還值得檢查的是它不會凌駕於其他互動式內容之上而無法將其忽略。這在鍵盤測試中會咬你一口。 CSS 錨點定位:我認為這是未來的發展方向 CSS Anchor Positioning 是我目前最感興趣的方向。當我第一次看到它時,我不確定該規範中有多少內容是實際可用的。它允許您直接在 CSS 中聲明下拉式選單及其觸發器之間的關係,並由瀏覽器處理座標。 .trigger { 錨點名稱:--my-trigger; }

.dropdown-menu { 位置:絕對; 位置錨點:--my-trigger; 頂部:錨點(底部); 左:錨點(左); position-try-fallbacks:翻轉區塊、翻轉內聯; }

position-try-fallbacks 屬性使得它比手動計算更值得使用。瀏覽器在放棄之前會嘗試其他位置,因此視窗底部的下拉式選單會自動向上翻轉而不是被切斷。 基於 Chromium 的瀏覽器對瀏覽器的支援非常穩定, Safari 的支援也不斷增強。 Firefox 需要一個polyfill。 @oddbird/css-anchor-positioning 套件涵蓋了核心規範。我遇到了佈局邊緣情況,需要我沒有預料到的回退,所以將它視為漸進增強或將其與Firefox 的 JavaScript 後備。 簡而言之,有希望但尚未普及。在目標瀏覽器中進行測試。 就可訪問性而言,在 CSS 中聲明視覺關係並不會告訴可訪問性樹任何資訊。 aria-controls、aria-expanded、aria-haspopup-這部分仍由你負責。 有時解決方法只是移動元素 在到達入口網站或進行座標計算之前,我總是先問一個問題:這個下拉清單實際上需要位於滾動容器內嗎? 如果沒有,將標記移至更高層級的包裝器可以完全消除問題,無需 JavaScript,也無需進行座標計算。 這並不總是可能的。如果按鈕和下拉式選單封裝在​​同一個元件中,則移動一個元件而不移動另一個元件意味著重新考慮整個 API。但當你能做到的時候,就沒有什麼好調試的了。這個問題根本不存在。 現代 CSS 仍未解決的問題 CSS 在這方面已經取得了長足的進步,但仍有一些地方讓您失望。 立場:固定和改造問題仍然存在。它是故意出現在規範中的,這意味著不存在 CSS 解決方法。如果您使用的動畫庫將版面配置包裝在轉換後的元素中,那麼您又需要入口網站或錨點定位。 CSS 錨點定位很有前途,但卻是新事物。如同前面所提到的,在我寫這篇文章時,Firefox 仍然需要一個 polyfill。我遇到了佈局邊緣情況,需要我沒有預料到的回退。如果您現在需要在所有瀏覽器上保持一致的行為,那麼您仍然需要使用 JavaScript 來處理棘手的部分。 我實際上改變了我的工作流程的附加功能是 HTML Popover API,現在可在所有現代瀏覽器中使用。具有 popover 屬性的元素呈現在瀏覽器的頂層,位於所有內容之上,無需 JavaScript 定位。

對於工具提示、公開小部件和簡單的覆蓋等內容,轉義處理、點擊外部關閉和可靠的可訪問性語義都是免費的。這是我目前使用的第一個工具。 也就是說,它並不能解決定位問題。它解決了分層問題。您仍然需要錨點定位或 JavaScript 將彈出視窗與其觸發器對齊。 Popover API 處理分層。錨定位處理放置。一起使用,它們涵蓋了您以前需要圖書館做的大部分事情。 適合您情況的決策指南 在經歷了這一切之後,我現在對這個選擇的實際想法是這樣的。

使用門戶。當觸發器位於嵌套滾動容器深處時,我會使用它。我將此模式用於表格操作選單,並將其與焦點恢復和可訪問性檢查配對。這是最可靠的選擇,但需要預留額外接線的時間。 使用固定定位。這適用於當您使用普通 JavaScript 或輕量級框架並且可以驗證沒有祖先應用轉換或過濾器時。只要滿足這項限制,它的設定和調試都很簡單。 當您的瀏覽器支援允許時,請使用 CSS Anchor Positioning.Reach 來實現此目的。如果需要 Firefox 支持,請將其與 @oddbird polyfill 配對。這是該平台最終的發展方向,並將最終成為您的首選方法。 重構 DOM。當架構允許並且您希望運行時複雜度為零時使用此功能。我相信這可能是最被低估的選擇。 組合模式。當您希望將錨點定位作為主要方法,並與不受支援的瀏覽器的 JavaScript 後備配對時,請執行此操作。或用於 DOM 放置的入口網站與 getBoundingClientRect() 配對以提高座標精確度。

結論 我曾經將這個錯誤視為一次性問題——需要修補並繼續前進。但一旦我用它足夠長的時間來理解所涉及的所有三個系統——溢出裁剪、堆疊上下文和包含塊——它就不再感覺隨機了。我可以查看損壞的下拉清單並立即追蹤哪個祖先負責。我閱讀 DOM 方式的轉變才是真正的收穫。 沒有單一的正確答案。我所達到的目標取決於我可以在程式碼庫中控制什麼:當祖先樹不可預測時的門戶;乾淨簡單時固定定位;當沒有什麼能阻止我時移動元素;現在錨定位,我可以的地方。 無論您最終選擇什麼,都不要將可訪問性視為最後一步。根據我的經驗,這正是它被跳過的時候。 ARIA 關係、焦點管理、鍵盤行為——這些都不夠完美。它們是讓事情真正發揮作用的一部分。 查看我的 GitHub 儲存庫中的完整原始程式碼。 進一步閱讀 這些是我在處理此問題時不斷回顧的參考資料:

堆疊上下文 (MDN) 《CSS 錨點定位指南》,Juan Diego Rodriguez “Popover API 入門”,Godstime Aburu 浮動使用者介面 (floating-ui.com) CSS 溢位 (MDN)

You May Also Like

Enjoyed This Article?

Get weekly tips on growing your audience and monetizing your content — straight to your inbox.

No spam. Join 138,000+ creators. Unsubscribe anytime.

Create Your Free Bio Page

Join 138,000+ creators on Seemless.

Get Started Free