溢位時的回退選項和條件性隱藏

在使用 CSS 錨點定位時,一個重要的考慮因素是確保錨點定位的元素始終出現在方便使用者互動的位置(如果可能的話),而不管錨點位於何處。例如,當你滾動頁面時,錨點及其關聯的定位元素會向視口邊緣移動。當定位元素開始溢位視口時,你會希望改變它的位置,使其重新回到螢幕上,例如移動到錨點的另一側。

或者,在某些情況下,直接隱藏溢位的定位元素可能更可取——例如,如果它們的錨點在螢幕外,其內容可能就沒有意義了。

本指南介紹瞭如何使用 CSS 錨點定位機制來處理這些問題——位置嘗試回退選項position-try fallback options)和條件性隱藏conditional hiding)。位置嘗試回退選項為瀏覽器提供了備選位置,以便在定位元素開始溢位時嘗試放置,從而將它們保持在螢幕上。條件性隱藏允許指定在何種條件下隱藏錨點或定位元素。

備註: 關於 CSS 錨點定位的基礎知識,請參閱使用 CSS 錨點定位

功能摘要

如果一個工具提示框固定在某個 UI 元素的右上角,當用戶滾動內容,使得該 UI 功能移動到視口的右上角時,該 UI 功能的工具提示框就會滾動到螢幕外。CSS 錨點定位解決了這類問題。該模組的 position-try-fallbacks 屬性指定了一個或多個備選的位置嘗試回退選項,供瀏覽器嘗試,以防止定位元素溢位。

位置嘗試回退選項可以透過以下方式指定:

此外,position-try-order 屬性允許你指定各種選項,使得瀏覽器會優先選擇一個可用的位置嘗試選項,而不是元素的初始定位。例如,你可能希望最初將元素顯示在具有更多可用高度或寬度的空間中。

簡寫屬性 position-try 可用於在單個宣告中指定 position-try-orderposition-try-fallbacks 的值。

在某些情況下,如果錨點在螢幕外,錨點定位的內容就沒有意義了,反之亦然。例如,你可能有一個包含測驗問題的錨點,而答案包含在相關的定位元素中,你希望它們要麼一起顯示,要麼都不顯示。這可以透過條件性隱藏來實現,它由 position-visibility 屬性管理。該屬性接受多個值,用於定義在何種條件下隱藏溢位元素。

預定義的回退選項

position-try-fallbacks 屬性的預定義回退選項值(在規範中定義為 <try-tactic>)會在錨點定位的元素可能溢位時,沿一個或兩個軸線“翻轉”其位置。

元素可以設定為沿塊軸(flip-block)、內聯軸(flip-inline)翻轉,或者沿從錨點一角穿過其中心到對角的假想線對角翻轉(flip-start)。前兩個值會翻轉元素,將其位置映象到相對的一側,而 flip-start 則會映象到相鄰的一側。例如,如果一個定位在錨點上方 10px 的元素開始在錨點頂部溢位,flip-block 值會將其翻轉到錨點下方 10px 處。

在此示例中,我們包含兩個 <div> 元素。第一個將是我們的錨點元素,第二個將相對於錨點進行定位。

html
<div class="anchor">⚓︎</div>

<div class="infobox">
  <p>This is an information box.</p>
</div>

我們將 <body> 元素的樣式設定為比視口更大,以便我們可以在視口中水平和垂直地滾動錨點和定位元素。

css
body {
  width: 1500px;
  height: 500px;
}

為了便於說明,我們對錨點進行絕對定位,使其出現在初始 <body> 渲染的中心附近。

錨點定位的元素被賦予了固定定位,並使用 position-area 繫結到錨點的左上角。它被設定為 position-try-fallbacks: flip-block, flip-inline;,以便在錨點靠近視口邊緣時,為移動定位元素以防止其溢位提供一些回退選項。

css
.infobox {
  position: fixed;
  position-anchor: --my-anchor;
  position-area: top left;
  position-try-fallbacks: flip-block, flip-inline;
}

備註: 當指定多個位置嘗試回退選項時,它們用逗號分隔,並按指定的順序進行嘗試。

嘗試滾動演示,讓錨點開始靠近邊緣。

  • 將錨點移動到視口頂部。定位元素會翻轉到錨點的左下方以避免溢位。
  • 將錨點移動到視口左側。定位元素會翻轉到錨點的右上方以避免溢位。

如果將錨點移向視口的左上角,你會發現一個問題——當定位元素開始在塊和內聯方向上溢位時,它會翻轉回其預設的左上角位置,並在兩個方向上都溢位,這不是我們想要的。

發生這種情況是因為我們只給了瀏覽器 flip-block flip-inline 的位置選項。我們沒有給它同時嘗試兩者的選項。瀏覽器會嘗試這些回退選項,尋找一個能使定位元素完全呈現在視口或包含塊內部的選項。如果找不到,它會以其最初定義的渲染位置渲染定位元素,不應用任何位置回退選項。

下一節將演示如何解決這個問題。

將多個值組合成一個選項

可以將多個預定義的嘗試回退選項自定義嘗試選項名稱放入一個由空格分隔的嘗試回退選項值中,該值是逗號分隔的 position-try-fallbacks 列表的一部分。當嘗試應用這些回退選項時,瀏覽器會將各個效果組合成一個單一的組合回退選項。

讓我們使用一個組合的嘗試回退選項來解決我們在上一個演示中發現的問題。這個演示的 HTML 和 CSS 與之前相同,除了資訊框的定位樣式。在這種情況下,它被賦予了第三個位置嘗試回退選項:flip-block flip-inline

css
.infobox {
  position: fixed;
  position-anchor: --my-anchor;
  position-area: top left;
  position-try-fallbacks:
    flip-block,
    flip-inline,
    flip-block flip-inline;
}

這意味著瀏覽器會首先嚐試 flip-block,然後嘗試 flip-inline 來避免溢位。如果這兩個回退選項都失敗了,它會嘗試將兩者結合起來,同時在塊內聯方向上翻轉元素的位置。現在,當你將錨點向視口的頂部左側邊緣滾動時,定位元素會翻轉到右下角。

使用 position-area 嘗試回退選項

預定義的 <try-tactic> 嘗試回退選項很有用,但也有侷限性,因為它們只允許定位元素的位置沿軸線翻轉。如果你有一個錨點定位的元素位於其錨點的左上方,並希望在它開始溢位時將其位置更改為錨點的正下方,該怎麼辦呢?

為了實現這一點,你可以使用 position-area 值作為位置嘗試回退選項,將其包含在 position-try-fallbacks 列表中。這會自動建立一個基於該位置區域的嘗試回退選項。實際上,這是建立只包含該 position-area 屬性值的自定義位置選項的快捷方式。

以下示例展示了 position-area 位置嘗試回退選項的使用。我們使用相同的 HTML 和 CSS,除了資訊框的定位。在這種情況下,我們的位置嘗試回退選項是 position-area 值——toptop-rightrightbottom-rightbottombottom-leftleft。無論錨點接近哪個視口邊緣,定位元素都會被合理地定位。這種詳細的方法比預定義值的方法更精細、更靈活。

css
.infobox {
  position: fixed;
  position-anchor: --my-anchor;
  position-area: top left;
  position-try-fallbacks:
    top, top right, right,
    bottom right, bottom,
    bottom left, left;
}

備註: 你不能將 position-area 嘗試回退選項新增到位置嘗試回退列表中的由空格分隔的組合位置選項中。

滾動頁面,看看當錨點靠近視口邊緣時這些位置嘗試回退選項的效果。

自定義回退選項

要使用上述機制無法實現的自定義位置回退選項,你可以使用 @position-try @規則建立自己的選項。語法如下:

@position-try --try-fallback-name {
  descriptor-list
}

--try-fallback-name 是開發者為位置嘗試回退選項定義的名稱。然後,這個名稱可以在 position-try-fallbacks 屬性值的逗號分隔的嘗試回退選項列表中指定。如果多個 @position-try 規則具有相同的名稱,則文件順序中最後一個會覆蓋其他的。避免為你的嘗試回退選項你的錨點或自定義屬性名稱使用相同的名稱;這不會使 @規則無效,但會讓你的 CSS 非常難以理解。

descriptor-list 定義了該單個自定義嘗試回退選項的屬性值,包括定位元素應如何放置和調整大小,以及任何外邊距。允許的屬性描述符的有限列表包括:

如果你命名的自定義嘗試回退選項被應用,@規則中包含的值會應用到定位元素上。如果這些屬性之前已經在定位元素上設定過,那麼這些屬性值會被描述符的值覆蓋。如果使用者滾動,導致應用了不同的嘗試回退選項或沒有應用嘗試回退選項,則先前應用的嘗試回退選項的值將被取消設定。

在這個例子中,我們設定並使用了幾個自定義的嘗試回退選項。我們使用了與之前例子相同的基本 HTML 和 CSS 程式碼。

我們首先使用 @position-try 定義了四個自定義嘗試回退選項:

css
@position-try --custom-left {
  position-area: left;
  width: 100px;
  margin-right: 10px;
}

@position-try --custom-bottom {
  position-area: bottom;
  margin-top: 10px;
}

@position-try --custom-right {
  position-area: right;
  width: 100px;
  margin-left: 10px;
}

@position-try --custom-bottom-right {
  position-area: bottom right;
  margin: 10px 0 0 10px;
}

一旦我們的自定義嘗試回退選項被建立,我們就可以透過引用它們的名稱將它們包含在位置列表中。

css
.infobox {
  position: fixed;
  position-anchor: --my-anchor;
  position-area: top;
  width: 200px;
  margin-bottom: 10px;
  position-try-fallbacks:
    --custom-left, --custom-bottom, --custom-right, --custom-bottom-right;
}

請注意,我們的預設位置由 position-area: top 定義。當資訊框沒有在任何方向上溢位頁面時,資訊框位於錨點上方,並且在 position-try-fallbacks 屬性中設定的位置嘗試回退選項會被忽略。另外,請注意資訊框設定了固定的寬度和底部外邊距。這些值會隨著應用不同的位置嘗試回退選項而改變。

如果資訊框開始溢位,瀏覽器會嘗試 position-try-fallbacks 屬性中列出的位置選項。

  • 瀏覽器首先嚐試 --custom-left 回退位置。這會將資訊框移動到錨點的左側,調整外邊距以適應,併為資訊框設定不同的寬度。
  • 接下來,瀏覽器嘗試 --custom-bottom 位置。這會將資訊框移動到錨點的底部,並設定適當的外邊距。它不包含 width 描述符,所以資訊框會恢復到由 width 屬性設定的預設寬度 200px
  • 瀏覽器接著嘗試 --custom-right 位置。這與 --custom-left 位置的工作方式非常相似,應用了相同的 width 描述符值,但 position-areamargin 的值是映象的,以將資訊框適當地放置在右側。
  • 如果其他回退選項都未能阻止定位元素溢位,瀏覽器會嘗試 --custom-bottom-right 位置作為最後的手段。這與其他回退選項的工作方式非常相似,但它將定位元素放置在錨點的右下方。

如果所有回退選項都未能阻止定位元素溢位,位置將恢復為初始的 position-area: top; 值。

備註: 當應用一個位置嘗試回退選項時,它的值將覆蓋在定位元素上設定的預設值。例如,定位元素上設定的預設 width200px,但是當應用 --custom-right 位置嘗試回退選項時,它的寬度被設定為 100px

滾動頁面,看看當錨點靠近視口邊緣時這些位置嘗試回退選項的效果。

使用 position-try-order

position-try-order 屬性的關注點與位置嘗試功能的其他部分略有不同,因為它在定位元素首次顯示時使用位置嘗試回退選項,而不是在它正在溢位時。

此屬性允許你指定希望定位元素最初使用能為其包含塊提供最大寬度或最大高度的位置嘗試回退選項來顯示。這可以透過設定 most-heightmost-widthmost-block-sizemost-inline-size 值來實現。你還可以使用 normal 值來移除任何先前設定的 position-try-order 值的效果。

如果沒有可用的位置嘗試回退選項能提供比分配給元素的初始定位更大的寬度/高度,position-try-order 將沒有效果。

讓我們來看一個演示,展示這個屬性的效果。HTML 與之前的例子相同,只是我們添加了一個包含單選按鈕的 <form>,允許你選擇不同的 position-try-order 值來檢視它們的效果。

我們包含一個自定義的嘗試回退選項——--custom-bottom——它將元素定位在錨點下方並新增一個外邊距。

css
@position-try --custom-bottom {
  top: anchor(bottom);
  bottom: unset;
  margin-top: 10px;
}

我們最初將資訊框定位在錨點的頂部,然後給它我們的自定義嘗試回退選項。

css
.infobox {
  position: fixed;
  position-anchor: --my-anchor;
  bottom: anchor(top);
  margin-bottom: 10px;
  justify-self: anchor-center;
  position-try-fallbacks: --custom-bottom;
}

最後,我們包含一些 JavaScript,它在單選按鈕上設定一個 change 事件處理程式。當一個單選按鈕被選中時,它的值會應用到資訊框的 position-try-order 屬性上。

js
const infobox = document.querySelector(".infobox");
const radios = document.querySelectorAll('[name="position-try-order"]');

for (const radio of radios) {
  radio.addEventListener("change", setTryOrder);
}

function setTryOrder(e) {
  const tryOrder = e.target.value;
  infobox.style.positionTryOrder = tryOrder;
}

嘗試選擇 most-height 排序選項。這會應用 --custom-bottom 位置嘗試回退選項,將元素定位在錨點下方。這是因為錨點下方的空間比上方的空間要多。

條件性隱藏錨點定位元素

在某些情況下,你可能想要隱藏一個錨點定位的元素。例如,如果錨點元素因為離視口邊緣太近而被裁剪,你可能只想完全隱藏其關聯的元素。position-visibility 屬性允許你指定在何種條件下隱藏定位元素。

預設情況下,定位元素是 always(總是)顯示的。no-overflow 值會在定位元素開始溢位其包含元素或視口時強制隱藏該元素。

另一方面,anchors-visible 值會在其關聯的錨點完全隱藏時強制隱藏定位元素,無論是透過溢位其包含元素(或視口)還是被其他元素覆蓋。只要錨點的任何部分仍然可見,定位元素就會是可見的。

一個被強制隱藏的元素表現得就像它及其後代元素都被設定了 visibility 值為 hidden,無論它們實際的 visibility 值是什麼。

讓我們看看這個屬性的實際效果。

這個例子使用了與之前例子相同的 HTML 和 CSS,資訊框被繫結到錨點的下邊緣。資訊框被賦予 position-visibility: no-overflow;,以便在它向上滾動到開始溢位視口時完全隱藏它。

css
.infobox {
  position: fixed;
  position-anchor: --my-anchor;
  margin-bottom: 5px;
  position-area: top span-all;
  position-visibility: no-overflow;
}

向下滾動頁面,注意一旦定位元素到達視口頂部,它就會被隱藏。

另見