建立 CSS 輪播
CSS overflow 模組定義了一些特性,可以用來建立靈活且無障礙的純 CSS 輪播,它帶有瀏覽器生成且可由開發者設定樣式的滾動按鈕和滾動標記。本指南將解釋如何使用這些特性來建立一個輪播。
輪播概念
輪播是 Web 上的一個常見功能。它們通常以滾動內容區域的形式出現,其中包含多個專案,例如演示幻燈片、廣告、頭條新聞或關鍵產品特性。
使用者可以透過點選或啟用導航按鈕或透過滑動來瀏覽這些專案。導航通常包括:
-
通常是“上一個”和“下一個”按鈕或連結。
- 滾動標記
-
一系列按鈕或連結圖示,每個圖示代表一個或多個專案,具體取決於輪播內每個滾動位置顯示的專案數量。
輪播的一個關鍵特性是分頁——專案感覺像是獨立的內容片段,使用者在它們之間移動,而不是形成一個連續的內容部分。你可以一次顯示一個專案,或者在每個輪播“頁面”上顯示多個專案。當多個專案可見時,每次按下“下一個”或“上一個”按鈕時,你可能會顯示一組全新的專案。或者,你可以在列表的一端新增一個新專案,同時將另一端的專案移出檢視。
使用 JavaScript 實現輪播可能相當脆弱且具有挑戰性。它需要指令碼將滾動標記與它們所代表的專案關聯起來,同時不斷更新滾動按鈕以保持其正常執行。當使用 JavaScript 建立輪播時,還必須額外新增輪播和相關控制元件的無障礙性。
幸運的是,我們可以使用 CSS 輪播特性建立帶有相關控制元件的無障礙輪播,而無需使用 JavaScript。
CSS 輪播特性
CSS 輪播特性提供了一些偽元素和偽類,使得僅使用 CSS 和 HTML 就可以建立輪播,瀏覽器以一種無障礙、靈活且一致的方式處理大部分滾動和連結引用。這些特性如下:
-
這些偽元素在滾動容器內部生成,代表滾動按鈕,用於將容器向指定方向滾動。
::scroll-marker-group-
在滾動容器內部生成;用於收集和佈局滾動標記。
::scroll-marker-
在滾動容器祖先元素的子元素內部或滾動容器的列內生成,以表示它們的滾動標記。可以選擇這些標記將容器滾動到其關聯的子元素或列,它們被收集在滾動容器的
::scroll-marker-group中以便進行佈局。 :target-current-
這個偽類可以用來選擇當前啟用的滾動標記。它可以用來為當前啟用的標記提供高亮樣式,這對於可用性和無障礙性非常重要。
::column-
當一個容器透過 CSS 多列布局 設定為多列顯示其內容時,此偽元素代表生成的各個列。它可以與
::scroll-marker結合使用,為每一列生成一個滾動標記。
單頁輪播
我們的第一個示例是一個單頁輪播,每個專案佔據整個頁面。我們有滾動標記作為底部導航,以及頁面兩側的滾動按鈕,使使用者能夠移動到下一頁和上一頁。
我們將使用 flexbox 來佈局輪播,使用滾動捕捉來強制實現清晰的分頁,並使用錨點定位來定位滾動按鈕和滾動標記相對於輪播的位置。
HTML 由一個標題元素和一個無序列表組成,每個列表項都包含一些示例內容:
<h1>CSS carousel single item per page</h1>
<ul>
<li>
<h2>Page 1</h2>
</li>
<li>
<h2>Page 2</h2>
</li>
<li>
<h2>Page 3</h2>
</li>
<li>
<h2>Page 4</h2>
</li>
</ul>
使用 flexbox 進行輪播佈局
我們使用 flexbox 來建立一行專案;<ul> 是 flex 容器,其子列表項 <li> 水平顯示,每個專案佔據輪播的整個寬度。
無序列表透過 100vw 的 width 設定來填充視口的全寬;它還被賦予了 300px 的 height 和一些 padding。然後我們使用 flexbox 來佈局列表——將 display 值設定為 flex,使得子列表項以行顯示(由於預設的 flex-direction 值為 row),每個專案之間有 4vw 的 gap。
ul {
width: 100vw;
height: 300px;
padding: 20px;
display: flex;
gap: 4vw;
}
現在是時候為列表項設定樣式了。第一個宣告提供了基本的樣式。重要的宣告是 flex 值為 0 0 100%,這強制每個專案與容器(<ul>)一樣寬。結果,內容將溢位其容器,視口將水平滾動。
li {
list-style-type: none;
background-color: #eeeeee;
border: 1px solid #dddddd;
padding: 20px;
flex: 0 0 100%;
}
li:nth-child(even) {
background-color: cyan;
}
此外,每個偶數編號的列表項透過 :nth-child() 被賦予了不同的背景顏色,以便更容易地看到滾動效果。
在列表上設定滾動捕捉
在本節中,我們將在 <ul> 上設定一個 overflow 值,將其變成一個滾動容器,然後應用 CSS 滾動捕捉,使列表在內容滾動時捕捉到每個列表項的中心。
在 <ul> 上設定 overflow-x 值為 scroll,使其內容在列表內水平滾動,而不是整個視口滾動。然後使用 CSS 滾動捕捉來捕捉到每個“頁面”——設定 scroll-snap-type 值為 x mandatory,使列表成為一個滾動捕捉容器。關鍵字 x 會使容器的捕捉目標在水平方向上被捕捉,而關鍵字 mandatory 意味著容器在滾動動作結束時總是會捕捉到一個捕捉目標。
ul {
overflow-x: scroll;
scroll-snap-type: x mandatory;
}
接下來,在列表項上設定 scroll-snap-align 值為 center,這樣當列表滾動時,它會捕捉到每個列表項的中心。
li {
scroll-snap-align: center;
}
到目前為止顯示的程式碼呈現如下:
嘗試透過滑動或使用捲軸來滾動列表,以檢視滾動捕捉的效果。無論你在哪裡結束滾動動作,一個專案總是會“捕捉”到位。
注意: 使用 CSS 輪播特性並非強制要求使用 CSS 滾動捕捉。但是,包含了滾動捕捉的輪播效果要好得多。如果沒有滾動捕捉,滾動按鈕和標記將不太可能在頁面之間乾淨地導航,結果會不盡如人意。
建立滾動按鈕
在本節中,我們將在示例中新增“上一個”和“下一個”滾動按鈕,以提供一個在輪播頁面之間導航的工具。這是透過使用 ::scroll-button() 偽元素實現的。
::scroll-button() 偽元素僅當其 content 屬性被設定為除 none 之外的值時,才會在滾動容器內部生成按鈕。每個 ::scroll-button() 代表一個滾動按鈕,其滾動方向由選擇器的引數指定。每個滾動容器最多可以生成四個滾動按鈕,每個按鈕將容器的內容向塊軸或行內軸的開始或結束方向滾動。
你還可以指定一個 * 引數來用樣式定位所有的 ::scroll-button() 偽元素。
首先,為所有滾動按鈕設定一些基本樣式,以及基於不同狀態的樣式。為鍵盤使用者設定 :focus 樣式是很重要的。此外,由於滾動按鈕在無法再向該方向滾動時會自動設定為 disabled,我們使用 :disabled 偽類來定位這種狀態。
ul::scroll-button(*) {
border: 0;
font-size: 2rem;
background: none;
color: black;
opacity: 0.7;
cursor: pointer;
}
ul::scroll-button(*):hover,
ul::scroll-button(*):focus {
opacity: 1;
}
ul::scroll-button(*):active {
translate: 1px 1px;
}
ul::scroll-button(*):disabled {
opacity: 0.2;
cursor: unset;
}
接下來,透過 content 屬性為左、右滾動按鈕設定適當的圖示,這也是導致滾動按鈕生成的原因:
ul::scroll-button(left) {
content: "◄";
}
ul::scroll-button(right) {
content: "►";
}
定位滾動按鈕
我們已經建立了滾動按鈕。現在我們將使用 CSS 錨點定位將它們相對於輪播進行定位。
首先,在列表上設定一個參考 anchor-name。接下來,每個滾動按鈕的 position 設定為 absolute,其 position-anchor 屬性設定為在列表上定義的相同參考名稱,以將兩者關聯起來。
ul {
anchor-name: --my-carousel;
}
ul::scroll-button(*) {
position: absolute;
position-anchor: --my-carousel;
}
為了實際定位每個滾動按鈕,我們在它們的內邊距屬性上設定值。我們使用 anchor() 函式來定位按鈕的指定邊相對於輪播的邊。在每種情況下,都使用 calc() 函式在按鈕邊緣和輪播邊緣之間新增一些空間。例如,左滾動按鈕的右邊緣被定位在輪播左邊緣向右 70 畫素的位置。
ul::scroll-button(left) {
right: calc(anchor(left) - 70px);
bottom: calc(anchor(top) + 13px);
}
ul::scroll-button(right) {
left: calc(anchor(right) - 70px);
bottom: calc(anchor(top) + 13px);
}
加入滾動按鈕的程式碼後,我們得到以下結果:
嘗試按下“上一個”和“下一個”滾動按鈕,看看頁面是如何滾動的,同時遵循滾動捕捉的行為。還要注意,當列表滾動到內容開始處時,“上一個”按鈕是如何自動停用的,而當列表滾動到內容結束處時,“下一個”按鈕是如何自動停用的。
建立滾動標記
滾動標記是一組按鈕,每個按鈕都將輪播滾動到與其中一個內容頁面相關的位置。它們提供了額外的導航工具,同時也指示了你在輪播頁面中的進度。
在本節中,我們將向輪播新增滾動標記,這涉及三個主要功能:
scroll-marker-group屬性設定在滾動容器元素上。它需要被設定為一個非none的值,以便生成::scroll-marker-group偽元素;它的值指定了滾動標記組在輪播的 Tab 順序和佈局盒順序中的出現位置(但不是 DOM 結構)——before將其放在開始處,滾動按鈕之前,而after將其放在末尾。::scroll-marker-group偽元素存在於滾動容器內部,用於將滾動標記作為一個整體進行收集、包含和佈局。::scroll-marker偽元素存在於滾動容器祖先元素的子元素和::column片段內部,並代表它們的滾動標記。這些標記被收集在祖先元素的::scroll-marker-group中以便進行佈局。
首先,列表的 scroll-marker-group 屬性被設定為 after,這樣 ::scroll-marker-group 偽元素就會在焦點和佈局盒順序中被放置在列表的 DOM 內容之後;這意味著它位於滾動按鈕之後:
ul {
scroll-marker-group: after;
}
注意:或者,可以使用 scroll-target-group 從一個包含一組 <a> 元素的現有元素建立一個滾動標記組容器。
接下來,列表的 ::scroll-marker-group 偽元素使用 CSS 錨點定位相對於輪播進行定位,類似於滾動按鈕,但它在輪播上水平居中,使用 justify-self 值為 anchor-center。該組使用 flexbox 進行佈局,justify-content 值為 center,gap 為 20px,這樣其子元素(::scroll-marker 偽元素)就會在 ::scroll-marker-group 內部居中,並且每個元素之間有間隙。
ul::scroll-marker-group {
position: absolute;
position-anchor: --my-carousel;
top: calc(anchor(bottom) - 70px);
justify-self: anchor-center;
display: flex;
justify-content: center;
gap: 20px;
}
接下來,我們處理滾動標記本身的外觀和感覺;它們可以像任何其他生成內容一樣進行樣式設定。需要注意的是,我們需要為 content 屬性設定一個非 none 的值,以便實際生成滾動標記。我們還設定了一些基本樣式,使標記顯示為帶輪廓的圓形:
li::scroll-marker {
content: "";
width: 16px;
height: 16px;
background-color: transparent;
border: 2px solid black;
border-radius: 50%;
}
注意: 生成的內容預設是內聯的;我們可以對我們的滾動標記應用 width 和 height,因為它們是作為 flex 專案進行佈局的。
最後,對於本節,使用 :target-current 偽類來選擇與當前可見“頁面”相對應的滾動標記,突出顯示使用者在內容中滾動的進度。在這種情況下,我們將 background-color 設定為 black,使其樣式為一個實心圓。
li::scroll-marker:target-current {
background-color: black;
}
單頁輪播最終效果
所有上述程式碼結合在一起,建立了以下結果:
自上一個即時示例以來,已經添加了滾動標記——嘗試按下它們直接跳轉到每個頁面。請注意當前標記是如何高亮的,這樣你就可以看到你在分頁中的位置。也請嘗試使用 Tab 鍵切換到滾動標記組,然後使用游標鍵迴圈瀏覽每個頁面。
你還可以透過向左和向右滑動、拖動捲軸或按下滾動按鈕來在頁面之間導航。
響應式輪播:每頁多個專案
第二個示例是一個每頁有多個專案的輪播,它同樣包含了滾動按鈕和滾動標記用於在頁面間導航。這個示例也是響應式的——根據視口的寬度,每個頁面上會出現不同數量的專案。
這個示例與單頁輪播示例非常相似,不同之處在於它不使用 flexbox 進行佈局,而是使用 CSS 多列布局和 ::column 偽元素來建立跨越輪播整個寬度的任意列,這些列可能包含多個專案。
使用這種方法,我們可以確保如果視口變大或變小,而專案大小保持不變,我們永遠不會有部分專案顯示在滾動埠的邊緣之外。在這種情況下,滾動標記是按列在滾動容器片段上建立的,而不是按項在子元素上建立的。
HTML 與上一個示例非常相似,只是列表項的數量要多得多,而且由於一次會顯示多個專案,我們將其標記為專案而不是頁面:
...
<li>
<h2>Item 1</h2>
</li>
...
這個示例的 CSS 也非常相似,除了在以下部分中解釋的規則外。
使用列布局的輪播
這個示例使用 CSS 多列布局,而不是 flexbox,來佈局輪播專案。columns 值為 1 強制每列都佔據容器的整個寬度,內容一次顯示一列。還應用了 text-align 值為 center,強制內容與列表中心對齊。
ul {
width: 100vw;
height: 300px;
padding: 10px;
overflow-x: scroll;
scroll-snap-type: x mandatory;
columns: 1;
text-align: center;
}
我們為列表項提供了基本的盒子樣式,然後應用佈局樣式,以允許一個或多個專案適應單個內容列,具體取決於視口寬度。隨著列表變寬或變窄,數量會動態變化。
li {
list-style-type: none;
display: inline-block;
height: 100%;
width: 200px;
background-color: #eeeeee;
border: 1px solid #dddddd;
padding: 20px;
margin: 0 10px;
text-align: left;
}
li:nth-child(even) {
background-color: cyan;
}
關鍵的佈局屬性如下:
display值為inline-block被設定為強制列表項並排排列,並使列表水平滾動。- 為它們設定了一個
200px的絕對width來控制它們的大小,這意味著一個或多個專案將適應一個隨著視口寬度增長和收縮的列。 - 為它們設定了
text-align值為left,以覆蓋父容器上設定的text-align: center,因此專案內容將左對齊。
scroll-snap-align 屬性現在設定在 ::column 偽元素上——這些偽元素代表由 columns 屬性生成的內容列——而不是列表項。我們想要捕捉到每個完整的列,而不是每個單獨的列表項,每次滾動操作都顯示所有新專案。
ul::column {
scroll-snap-align: center;
}
列滾動標記
在這個示例中,用於建立滾動標記的 CSS 與上一個示例幾乎相同,只是選擇器不同——滾動標記是在生成的 ::column 偽元素上建立的,而不是在列表項上。注意我們在這裡包含了兩個偽元素來在生成的列上生成滾動標記。
ul::column::scroll-marker {
content: "";
width: 16px;
height: 16px;
background-color: transparent;
border: 2px solid black;
border-radius: 10px;
}
ul::column::scroll-marker:target-current {
background-color: black;
}
響應式輪播最終效果
響應式輪播的渲染效果如下:
嘗試透過向左和向右滑動、使用捲軸、按下滾動按鈕以及按下滾動標記在不同頁面之間導航。功能與單頁 flexbox 示例類似,不同的是現在每個導航位置都有多個列表項;滾動標記設定在可能包含多個專案的列片段上,而不是每個專案上。
此外,嘗試調整螢幕寬度,你會看到列表內能容納的列表項數量發生了變化——因此生成的列數也發生了變化。隨著列數的變化,滾動標記的數量會動態更新,以便每個列都在滾動標記組中有所代表。
另見
- CSS overflow 模組
- CSS anchor positioning 模組
- CSS scroll snap 模組
- CSS 輪播庫 via chrome.dev (2025)