從 Chrome 125 開始,支持了一個全新的 CSS 特性 - Anchor Positioning,翻譯過來即是錨點定位。
在之前的文章中,我們較為系統(tǒng)的講述了這個新特性的使用,感興趣的可以翻開一下前文:搶先體驗!超強大的 Anchor Positioning 錨點定位。
在本文中,我們將使用錨點定位,實現(xiàn)一個簡化版本的 Popover 功能。下面,我們將一起一探究竟。
傳統(tǒng) Popover 功能
長話短說,在日常的前端需求開發(fā)中,有這么一種場景,我們需要 hover 某個具體元素,以彈出一個彈出層,像是這樣:

如果我們將彈出層的 DOM 結構,寫在某個具體的元素內(nèi)部,譬如放在與被 Hover 元素同級。如果被 Hover 元素的祖先元素的某一層設置了 overflow: hidden
,則可能會出現(xiàn)這種截斷現(xiàn)象:

為了避免這種情況的發(fā)生,一般情況下,常見的解決方案都是會將彈出層的 DOM,插入到頁面最外層的 <body>
容器之下,再通過實時計算位置,將該彈出層定位到被 hover 元素附近。這也就是類似于 popper.js、Tippy.js 等傳統(tǒng)庫所干的事情。
核心邏輯如下:
- 通過 JavaScript 獲取觸發(fā)元素的位置信息(getBoundingClientRect)
- 計算視口剩余空間(需考慮滾動位置、窗口尺寸等)
- 動態(tài)調(diào)整 Popover 位置(需處理邊界碰撞、翻轉邏輯)
- 添加 resize/scroll 事件監(jiān)聽器進行位置修正
其核心在于,通過 Javascript 動態(tài)計算當前 hover 元素的位置,將彈出層定位到當前 hover 元素附近合適的位置,并且處理好各種邊界場景,這里很重要一點是強依賴于 Javascript 的計算。
因為傳統(tǒng)的 CSS,是沒有辦法改變元素的定位父元素的能力的。
而有趣的是,全新的 CSS 特性 - Anchor Positioning 錨點定位就是為了解決這個問題的。
快速了解 Anchor Positioning 錨點定位
那,到底什么是 Anchor Positioning 錨點定位呢?我們通過一個 DEMO 快速上手。
假設我們在頁面上有這么三個元素:按鈕A、按鈕B、被定位元素C,其中被 元素C 是一個插入到任意地方,且為絕對定位的元素,核心 CSS 代碼如下:
<body>
<div class="btn-a">按鈕A</div>
<div class="btn-b">按鈕B</div>
<div class="anchor">被定位元素C</div>
</div>
.btn-a, .btn-b {
// 高寬各類樣式
}
.anchor {
position: absolute;
}
現(xiàn)在,三個元素屬于同級關系,且 C 元素是絕對定位,當前基于 body
進行絕對定位。
大致樣式如下:

而錨點定位的屬性的核心作用就是,能夠改變元素定位的基準,增強元素的絕對定位的能力。Anchor Positioning(錨點定位)允許我們基于其它錨點元素的位置和尺寸去定位上下文,而不是傳統(tǒng)意義上的基于父元素去進行絕對定位。
下面,我們利用錨點定位去實現(xiàn),當兩個按鈕 A、B 被 Hover 的時候,讓定位元素 C 基于當前被 Hover 的按鈕元素進行絕對定位,核心 CSS 代碼如下:
.btn-a {
// 將元素聲明為定位基準點,命名為 --btn-a
anchor-name: --btn-a;
}
.btn-b {
// 將元素聲明為定位基準點,命名為 --btn-b
anchor-name: --btn-b;
}
// 當元素被 hover 的時候,改變 C 元素的樣式
.btn-a:hover ~ .anchor {
// 錨點綁定,建立元素與錨點的定位關系
position-anchor: --btn-a;
// 基于新的錨點元素,設置元素的 left\top 屬性
left: anchor(--btn-a center);
top: anchor(--btn-a bottom);
// 利用 transform 輕微調(diào)整定位,非核心代碼
transform: translate(-50%, 5px);
}
// 同理,與上面做的事情一致,知識在 hover 按鈕 B 時,重新設定錨點元素
.btn-b:hover ~ .anchor {
position-anchor: --btn-b;
left: anchor(--btn-b center);
top: anchor(--btn-b bottom);
transform: translate(-50%, 5px);
}
.anchor {
// 方便動圖演示,增加元素過渡動畫,非核心代碼
transition: all .2s;
}
核心關注上面的 anchor-name
、position-anchor
、 anchor
幾個屬性,其作用和含義在注釋中有說明,在下文還會再描述一次。
如此一來,我們就實現(xiàn)了動態(tài)改變 C 元素定位基準的能力,我們看看效果:

簡單而言,我們利用錨點定位的能力,在 hover 按鈕 A\B 的時候,把 C 元素定位在它們正下方。這個就是錨點定位的能力!
Anchor Positioning 錨點定位核心 API
(1)anchor-name:錨點定義
功能:將元素聲明為定位基準點
語法:anchor-name: <dashed-ident>;
應用場景:觸發(fā)元素、參考元素、動態(tài)定位源
.trigger-btn {
anchor-name: --menu-anchor;
}
(2)position-anchor:錨點綁定
功能:建立元素與錨點的定位關系
語法:position-anchor: <dashed-ident>;
注意:需配合定位屬性(position: fixed/absolute)使用
.tooltip {
position: fixed;
position-anchor: --menu-anchor;
}
(3)anchor():動態(tài)定位
功能:根據(jù)錨點位置計算坐標
語法:anchor(<anchor-name>? <anchor-side>)
方位參數(shù):
- 垂直:top/center/bottom
- 水平:left/center/right
- 組合:top-left/bottom-right 等
.context-menu {
top: anchor(--ctx-anchor bottom);
left: anchor(--ctx-anchor right);
}
.tooltip {
left: anchor(center);
right: anchor(center);
}
(4)anchor-size():尺寸繼承
功能:獲取錨點元素的尺寸值
語法:anchor-size(<anchor-name>? <dimension>)
維度參數(shù):width/height/block/inline
.popover {
width: anchor-size(width);
min-height: calc(anchor-size(height) * 1.5);
}
這里介紹了錨點定位中最為核心的幾個屬性,掌握了這幾個屬性,就可以應付大部分場景了。在我之前的一篇入門文章中,對它們也有一些更為詳細的描述,感興趣的同學,可以翻看:搶先體驗!超強大的 Anchor Positioning 錨點定位
錨點定位的候補位置
還有一個非常重要的點,傳統(tǒng)的 Popover 組件,一般都會有這么個功能 -- 智能邊界處理。
我們以一個功能比較強大的 Popover 庫 floating-ui 舉例,其官網(wǎng)展示了如下的一個功能,當元素在滾動過程中,如果原本 Popover 彈窗被遮擋,會自動進行位置移動,將彈窗重新調(diào)整到可視區(qū)域,效果如下:

令人振奮的是,現(xiàn)在,CSS 的 Anchor Positioning 錨點定位同樣支持這種 智能邊界處理,在錨點定位中,我們稱之為候補位置。
這里,我們主要借助兩個錨點定位相關的屬性完成錨點定位的候補位置 position-try-fallbacks 和 @position-try 規(guī)則。
@position-try 規(guī)則
@position-try
用于定義一個備選定位策略(一組定位規(guī)則),可以在多個元素中復用。它的語法類似于定義一個命名規(guī)則集合。
@position-try --strategy-name {
top: anchor(bottom);
left: anchor(left);
}
關鍵點:
- 命名策略:每個 @position-try 規(guī)則需要唯一名稱(如 --tooltip-below)。
- 獨立作用域:策略內(nèi)部的定位規(guī)則獨立于元素自身的樣式,僅在被調(diào)用時生效。
position-try-fallbacks 屬性
position-try-fallbacks 用于在元素上指定備選定位策略的優(yōu)先級順序。瀏覽器會按順序嘗試這些策略,直到找到第一個可用的位置。
.element {
position-try-options: --strategy1, --strategy2, --strategy3;
}
關鍵點:
- 順序敏感:瀏覽器按列表順序嘗試策略,第一個可行的策略會被應用。
- 動態(tài)回退:如果所有策略均不可行,元素會回退到默認定位(或父容器約束)。
使用錨點定位實現(xiàn)候補位置
基于上述介紹,我們來實現(xiàn)一個基于錨點定位的候補位置。
假設我們?nèi)缦陆Y構,當前已經(jīng)使用了錨點定位:
<body>
<div class="btn">Reference</div>
<div class="anchor">Popover彈窗元素</div>
</div>
核心 CSS 如下:
.btn {
anchor-name: --btn;
border: 1px dashed #000;
background: #ddd;
}
.anchor {
position: absolute;
position-anchor: --btn;
left: anchor(--btn-a center);
top: anchor(--btn-a bottom);
transform: translate(-50%, 5px);
}
此時,兩個元素都插入在 <body>
下面,但是 Popover 彈窗元素使用了 position-anchor: --btn
錨點定位,使其絕對定位的父元素是 .btn
,并且,定位在按鈕的下方,效果如下:

此時,我們只需要再借助 position-try-fallbacks
和 @position-try
,實現(xiàn)候補位置:
@position-try
定義一個候補規(guī)則position-try-fallbacks
引入候補規(guī)則
核心 CSS 代碼如下:
.btn {
anchor-name: --btn;
border: 1px dashed #000;
background: #ddd;
}
.anchor {
position: absolute;
position-anchor: --btn;
left: anchor(--btn-a center);
top: anchor(--btn-a bottom);
transform: translate(-50%, 5px);
position-try-fallbacks: --position-bottom;
}
@position-try --position-bottom {
left: anchor(--btn center);
bottom: anchor(--btn top);
top: unset;
margin-bottom: 10px;
}
這樣,我們在滾動頁面的過程中,如果彈窗 popover 有超出視窗,候補規(guī)則會自動生效,看看效果:

仔細觀察,和上面利用 Javascript 庫實現(xiàn)的智能定位,效果一致,只是此時,我們僅僅使用了寥寥幾行 CSS 代碼!Amazing!
綜上所述,到今天,我們已經(jīng)可以利用 CSS 錨點定位大致實現(xiàn)一個極簡版的 Popover 彈窗,并且可以滿足大部分場景。不得不感嘆 CSS 確實愈發(fā)的強力,
當然,本文介紹的關于錨點定位的功能都是基于實現(xiàn)一個最小版本的 Popover 展開的,基于錨點定位的 API 和回退候補,還有更多有趣的內(nèi)容,感興趣的可以猛戳 MDN 進行了解:MDN - Anchor Positioning、MDN-Anchor Positioning @position try。
轉自https://www.cnblogs.com/coco1s/p/18739625
該文章在 2025/3/3 9:03:11 編輯過