「下拉顯示」是一種 UI 模式,在初始載入時,內容 (例如搜尋列或重新整理控制項) 會隱藏在可捲動區域的頂端,使用者可以向下拉動 (向上捲動) 來顯示內容。這種模式常用於行動應用程式和網頁應用程式,適合用於搜尋列、篩選器和其他次要控制項,這些控制項應可存取,但不應立即顯示。
CSS 屬性 scroll-initial-target 提供宣告式、僅限 CSS 的方式來實作這個模式。在主要內容元素上設定 scroll-initial-target: nearest 後,捲動容器會算繪隱藏的內容,並將其捲動至檢視畫面外。過去,開發人員必須仰賴 JavaScript (Element.scrollIntoView()) 或網址片段 ID (#content-id) 才能達到這個目的,但這兩種做法都有其限制,且實作難度較高。
導入方式
如要實作下拉顯示模式:
- 確保有捲動容器:目標元素必須位於捲動容器內 (具有溢位且允許捲動的元素,例如
overflow: auto)。這可以是任何祖系元素,包括根<html>元素。 - 定義隱藏元素:將要隱藏的內容 (例如搜尋列) 放在捲動容器內,做為第一個子系。初始載入時,這個元素會捲動至檢視畫面外。
- 定義主要內容:將主要內容元素緊接在隱藏元素之後。使用者應先看到這個元素。
- 指定主要內容:將
scroll-initial-target: nearest套用至主要內容元素,讓捲動容器在捲動至檢視畫面時顯示,並隱藏上方的元素。 - 新增捲動貼齊:為確保隱藏元素一律完全顯示或完全隱藏 (不會停留在部分捲動狀態),請在捲動容器中加入
scroll-snap-type: y mandatory,並在隱藏元素和主要內容元素中加入scroll-snap-align: start。
程式碼範例:下拉顯示搜尋
/**
* ANCESTOR: Define the scroll container.
* Scroll snapping ensures the search bar is always
* either fully visible or fully hidden.
*/
.scroll-container {
height: 100vh;
overflow-y: auto;
scroll-snap-type: y mandatory;
}
/**
* HIDDEN ELEMENT: The search bar hidden above the fold on load.
* It has scroll-snap-align so it snaps into place when pulled down.
*/
.search-bar {
height: 60px;
scroll-snap-align: start;
}
/**
* MAIN CONTENT + TARGET: The element the user sees first.
* scroll-snap-align makes it a valid snap point.
* scroll-initial-target tells the browser to scroll here on
* initial render, hiding the search bar above it.
*/
.main-content {
scroll-snap-align: start;
scroll-initial-target: nearest;
}
策略執行與最佳做法
- 建議:如要在載入時將使用者的注意力吸引到可捲動區域的特定部分,並刻意隱藏周邊 UI 單位 (例如最上方的搜尋列),請使用
scroll-initial-target: nearest。 - 請勿與無障礙功能焦點混淆。這項屬性只會移動「視覺」可視區域,不會移動鍵盤焦點。如果目標是鍵盤使用者的起點,您必須手動管理
element.focus()。 - 如果需要在載入時顯示流暢的「捲動」動畫,請不要使用這個屬性。這個屬性是離散的,會在版面配置階段立即設定位置。
- 請勿在同一個可捲動容器內的多個元素上設定
scroll-initial-target。如果有多個元素指定scroll-initial-target: nearest,瀏覽器會選取 DOM 樹狀結構順序中第一個出現的元素。 - 務必考量優先順序階層:網址片段 (例如
example.com/#top) 和容器層級的scroll-start屬性,優先順序都高於scroll-initial-target。
備用策略
如果瀏覽器不支援 API,請使用 JavaScript 備用方案。請注意,如要下拉內容來顯示,主要內容必須繫結至容器的 start (頂端)。
/**
* Progressive Enhancement Fallback
*/
document.addEventListener("DOMContentLoaded", () => {
// Check for native CSS support
if (!CSS.supports("scroll-initial-target", "nearest")) {
const targetContent = document.querySelector('.main-content.target');
if (targetContent) {
// Use behavior: "instant" to mimic the native CSS behavior
// 'block: start' should match your CSS 'scroll-snap-align' (or expected top position)
targetContent.scrollIntoView({ behavior: 'instant', block: 'start' });
}
}
});