錨定定位

放置工具提示或下拉式選單時,您通常會想將其放置在頁面上的另一個元素旁邊。雖然過去可使用絕對定位來達成此效果,但如果需求較複雜,通常會使用 JavaScript 定位項目。

CSS 錨點定位功能可讓您以宣告方式,將元素定位在另一個元素的相對位置。

繫結元素

如要將元素設為錨點,請為其提供以兩個破折號開頭的任何字串 anchor-name 值。這是定位元素用來尋找錨點的 ID,建議您為其提供描述性名稱。如果元素會以不同方式做為錨點使用,您甚至可以為元素提供多個錨點名稱。

您需要在定位元素上設定幾個屬性,才能繫結該元素。首先,您需要將元素從文件的流程中拉出,方法是設定 position: absoluteposition: fixed,讓元素浮動。

接著,您需要設定要繫結的錨點,方法是將 position-anchor 設為您在錨點上設定的錨點名稱。

最後,您需要設定錨點的放置位置。本單元稍後會進一步說明 position-area

#anchor {
   anchor-name: --my-anchor;
}

#positionedElement {
     position: absolute;
     position-anchor: --my-anchor;
     position-area: end;
}

隱含繫結

Popover 的繫結方式更簡單。使用含有 popovertarget 的按鈕開啟浮動視窗,或透過 showPopover({source}) 設定 source 時,浮動視窗已設定「隱含錨點」。由於彈出式視窗預設會隨著 position: fixed 浮動,因此如要設定彈出式視窗的位置,您只需要設定位置即可。

#anchor{}

#positionedElement {
  position-area: end;
  margin: unset;
}

設定潛在錨點的範圍

您可以在元件中實作錨點定位,以便在多個位置使用下拉式選單等模式。如果您多次使用相同的 anchor-name,如何確保每個定位元素都能找到正確的錨點?

JavaScript 解決方案需要為每個錨點新增專屬 ID,然後從定位元素參照該 ID。這樣做很麻煩,而 CSS 提供了更簡單的解決方案 anchor-scope

anchor-scope 屬性會設定要比對的錨點名稱,僅限元素及其後代。這個屬性接受一或多個錨點名稱的清單,或 all 關鍵字,以限制所有已定義錨點名稱的範圍。

理想情況下,anchor-scope 會新增至定位元素和錨點元素的祖先,且該祖先不得包含其他同名的錨點元素。通常位於可重複使用的元件根目錄。

以下範例顯示套用至具有相同 anchor-name 的重複元素時,anchor-scope 的差異。在本例中,所有 <img> 元素和圖片橫幅都會參照 --image 錨點名稱。將 anchor-scope 套用至 <li> 元素時,position-anchor: --image 只會比對與橫幅位於同一 <li> 元素中的 <img> 元素,否則會比對最後一個算繪的 <img>

位置

現在您已將元素繫結至錨點,接下來請放置元素。錨點定位提供兩種定位方法:position-areaanchor() 函式。

position-area

position-area 屬性可讓您指定一或兩個關鍵字,將元素放置在錨點周圍。這涵蓋許多常見用途,通常是良好的起點。

position-area 的運作方式

position-area 的運作方式是,在錨點和定位元素原始包含區塊的邊緣所產生的區域中,為定位元素建立新的包含區塊。

雖然 position-area 有許多關鍵字,但可以細分成幾個類別,方便瞭解。Anchor-tool.com 是探索語法的絕佳工具。

實體關鍵字

您可以使用實體關鍵字 topleftbottomrightcenter。舉例來說,position-area: top right 會將定位元素放在錨點上方和右側。這些關鍵字也有對應的實體軸,分別是 y-startx-starty-endx-end

邏輯關鍵字

您也可以使用邏輯關鍵字 block-startblock-endinline-startinline-end。舉例來說,在英文等語言中,position-area: block-end inline-start 會將定位元素放在錨點下方和左側;在文件的書寫模式中,則會將定位元素放在錨點的區塊軸上,以及錨點的內嵌軸上。center 也可以搭配邏輯關鍵字使用。

如果您指定邏輯關鍵字,也可以省略軸,但必須先指定區塊軸,再指定行內軸。position-area: start endposition-area: block-start inline-end 相同,甚至與 position-area: inline-end block-start 相同。

跨越多個格線區域

到目前為止,您可能已注意到這些選項只允許您將定位元素放在單一格線空間內。在實體或邏輯屬性中新增 span 前置字串,即可新增相鄰的中央格線空間。position-area: span-top right 會位於錨點右側,且從錨點底部到定位元素原始的包含區塊頂端。

下拉式選單的常見位置區域是 position-area: block-end span-inline-end

span-all 關鍵字會跨越 3 列或 3 欄。

單一關鍵字

如果您只設定一個關鍵字,系統會自動設定另一個軸。這項功能大致上會如您預期運作,但瞭解運作方式可能會有幫助。

如果提供的關鍵字清楚指出軸,系統會將另一個軸計算為 span-all。這表示 position-area: bottom 等於 position-area: bottom span-all,定位元素會位於錨點下方,並佔用整個可用寬度的包含區塊。

另一方面,如果關鍵字未明確指出軸,則會重複。position-area: start 等同於 start start,且會放置在錨點的左上角 (適用於從左至右書寫的語言)。

anchor() 函式

對於更進階的使用案例,position-area 可能無法滿足您的需求。anchor() 函式可讓您根據另一個元素的位置設定個別插邊屬性。這會解析為 CSS 長度,因此您可以在計算中使用,也可以搭配其他 CSS 函式使用。此外,您也可以將不同側邊繫結至不同錨點。

anchor() 函式會採用錨點名稱和錨點側邊。如果元素有預設錨點 (透過 position-anchor 設定或隱含設定,例如使用快顯視窗),則可省略錨點名稱。

.positionedElement {
  block-start: anchor(--my-anchor start);
  /*  OR  */
  position-anchor: --my-anchor;
  block-start: anchor(start);
}

備用值

如果找不到 anchor() 函式的錨點,整個宣告就會無效。如果錨點是在定位元素之後轉譯,或是沒有相符 anchor-name 的元素,就可能發生這種情況。如要處理這項問題,你可以設定備用長度或百分比。

.positionedElement {
   block-start: anchor(--my-anchor, 100px)
}

在上述範例中,定位元素的左值會錨定至 --focused-anchor,但只有在懸停或聚焦於第一個按鈕時,anchor-name 才會存在。由於 anchor() 函式會解析為長度,因此您可以使用另一個錨點做為備援。如果我們沒有提供備用值,定位元素就不會定位。

錨定側邊關鍵字

錨定側值會選擇要對齊的錨定邊緣。與 position-area 類似,錨點側邊值支援多種不同類型的語法。

類型 說明
實體 topleftbottomright

實體關鍵字對應錨點的特定側邊,但只能用於與您要設定的位置元素插邊相同的軸。

舉例來說,top: anchor(bottom) 會將元素頂端放在錨點底部,但 left: anchor(top) 無效。

側邊 insideoutside

inside 關鍵字對應於插邊屬性的同一側,而 outside 關鍵字則對應於同一軸上的另一側。

舉例來說,inset-block-start: anchor(inside) 是指錨點的 block-start 側,inset-inline-end: (outside) 則是錨點的 inline-start 側。

邏輯 startendself-startself-end

邏輯關鍵字會根據定位元素的書寫模式 (使用 self-startself-end),或定位元素所含區塊的書寫模式 (使用 startend),參照錨點的兩側。

百分比 0% - 100%

百分比值會沿著指定軸,將定位元素從錨點的起點到終點放置在軸上。0% 位於錨點的 start 側,而 100% 位於錨點的尾端。center 相當於 50%。如果您在結尾側插邊 (例如 bottom) 使用百分比,系統不會反轉,0% 仍是錨點的 start 側。

這個範例顯示百分比值一律會從指定軸的開頭到結尾:

使用anchor()

由於 anchor() 是長度,因此非常靈活。您可以使用 max()calc() 等 CSS 函式操控值。

其中一項限制是,您只能在插邊屬性上使用 anchor() 函式。

上例會在開啟的詳細資料面板後方新增背景,並在開啟不同面板時順暢地產生動畫效果,且會延展以納入懸停的詳細資料面板。為達成此目的,它會使用 min() 在兩個錨點之間挑選較短的長度。

#indicator{
/*  Use the smaller of the 2 values:  */
  inset-block-start: min(
/*   1. The start side of the default anchor, which is the open `<details>` element  */
    anchor(start),
/*   2. The start side of the hovered `<details>` element.    */
    anchor(--hovered start,
/*     If no `<details>` element is hovered, this falls back to infinity px, so that the other value is smaller, and therefore used.   */
       var(calc(1px * infinity)))
  );
}

這個範例也使用 calc() 在開啟的面板周圍新增內嵌空間。

使用錨點的大小

您也可以使用 anchor-size() 函式,將錨點的尺寸用於定位元素的尺寸、位置或邊界。

anchor-size() 會採用錨點名稱,或使用預設錨點。根據預設,系統會使用軸上錨點的大小,因此 width: anchor-size() 會傳回錨點的寬度。您也可以使用其他軸,指定所需長度,方法是使用實體關鍵字 widthheight,或是邏輯關鍵字 blockinlineself-blockself-inline

處理溢位

您已建立下拉式選單元件,並使用錨點定位將下拉式選單放在所需位置。但隨後您將選單移至畫面另一側,或將選單用於使用者選單,而使用者名稱過長。突然間,下拉式選單消失在螢幕外。現在該怎麼辦?

CSS 錨點定位內建系統,可讓您在定位元素超出其包含區塊時,快速建構一組強大的備援。

備用選項

position-try-fallbacks 規則會採用備援選項清單。如果預設位置溢位,系統會依序嘗試每個選項,直到找到不會溢位的位置為止。

您可以使用任何 position-area 值做為備援選項。在這個範例中,在英文等從左到右的書寫模式中,定位元素會嘗試定位在錨點底部,跨越中央和右側欄。如果溢位,系統會嘗試將其放置在錨點底部,並跨越左欄和中間欄。如果該位置也溢位,即使溢位,位置也會還原為預設位置。

.positioned-element {
  position-area: block-end span-inline-end;
  position-try-fallbacks: block-end span-inline-start;
}

此外,還有幾個 flip- 關鍵字可處理常見的回退情況。flip-blockflip-inline 嘗試在區塊和內嵌軸上翻轉元素。也可以與 flip-block flip-inline 結合使用,同時翻轉兩個軸。flip-start 值會將定位元素翻轉到錨點的起點和終點角落之間的對角線上。

您也可以使用 @position-try 建立自訂備援選項,設定邊界、對齊方式,甚至變更錨點。

@position-try --menu-below {
  position-area: bottom span-right;
  margin-top: 1em;
}

#positioned-element {
  position-try: --menu-below;
}

flip-blockflip-inline 可新增至 @position-try 備用選項,以建立變化版本。

#positioned-element {
  position-try: --menu-below, flip-inline --menu-below;
}

在上述範例中,瀏覽器會依序執行這些步驟,並在找到不會溢位的解決方案後立即停止。

  1. 元素會放置在錨點右下方的 position-area: end
  2. 如果溢位,系統會使用名為 --bottom-span-right 的自訂備援選項放置元素,也就是使用 position-area: bottom span-right 放置元素,並在下方加上額外邊界。
  3. 如果溢位,系統會使用 flip-inline --bottom-span-right 放置元素,將自訂備用選項與 flip-inline 合併,這基本上就是 position-area: bottom span-left
  4. 如果溢位,系統會使用--use-alternate自訂備援選項放置元素,也就是將元素放在完全不同的錨點下方。
  5. 如果溢位,元素會還原為原始位置,並顯示 position-area: end,即使溢位是已知問題也一樣。

備用順序

根據預設,如果初始位置溢位,瀏覽器會嘗試 position-try-fallbacks 中的每個選項,直到找到不會溢位的位置為止。您可以使用 position-try-order 覆寫這項行為,測試每個備用選項,並使用在指定軸上空間最大的選項。

您可以使用邏輯關鍵字 most-block-sizemost-inline-size,或實體關鍵字 most-heightmost-width 指定軸。

position-try-orderposition-try-fallbacks 可與 position-try 簡寫組合,順序在前。

捲動

使用者捲動頁面時,會希望頁面流暢移動。為達成此目的,瀏覽器會限制捲動時錨點定位的使用方式。

雖然您可以將已定位的元素繫結至不同捲動容器中的錨點,但元素只會因其中一個錨點捲動而移動。這會是預設錨點,也就是來自快顯視窗的隱含錨點,或是 position-anchor 的值。

請注意,即使錨點捲動到檢視區塊外,定位元素仍會保持顯示。如要在錨點隱藏時隱藏定位元素,請設定 position-visibility: anchors-visible。這不僅適用於錨點過度捲動的情況,也適用於以其他方式隱藏錨點的情況,例如使用 visibility: hidden

隨堂測驗

anchor() 中側邊的有效值為何?

inside
答對了!
25%
答對了!
25px
答錯了。雖然 25px 等長度可用做備用值,但只有百分比可用於側邊。
block-start
不正確
start
答對了!

哪些是 position-area 的有效值?

top
答對了!
block-end inline-end
答對了!
block-start block-end
答錯了。每個軸只能定義單一資料欄或資料列。

哪些屬性支援 anchor() 函式?

top
答對了!
margin-left
答錯了。
inset-block-start
答對了!
transform
答錯了。

如果有多個錨點具有相同的 anchor-name,會發生什麼情況?

系統會複製定位元素,並繫結至每個相符項目。
答錯了。
定位元素會繫結至文件中的第一個元素。
答錯了。
定位元素會繫結至文件中的最後一個元素。
答對了!
定位元素會繫結至最接近的錨點。
答錯了。