紀錄工作經驗、相關知識,解決技術相關問題。

CSS, HTML

CSS position 定位教學:static、relative、absolute、fixed、sticky 差異與實務範例

CSS position 定位教學:static、relative、absolute、fixed、sticky 差異與實務範例 特色圖片

在前端網頁切版中,只要遇到「右上角未讀紅點徽章」、「固定在畫面頂端的導覽列」、「右下角回到頂端按鈕」或是「滾動到一半自動吸附置頂的分類列」,就一定會用到 CSS position 定位屬性。

本文用最直白的方式整理 CSS position 的 5 個屬性值(static、relative、absolute、fixed、sticky),幫你一次搞懂它們各自是「相對於誰定位」以及為什麼有時候設了卻沒效果。


一張表看懂:5 種 CSS position 定位屬性差別

position 屬性值是否保留原本的排版空間?top / left 是相對於誰定位?最常見實務場景
static(預設值)是(正常文流)無效(忽略 top/left,z-index 也無作用,Flex/Grid 子項除外)一般普通段落與區塊
relative(相對定位)是(原位置保留空白)相對於「自己原本的位置」微調作為 absolute 子元素的定位基準父層
absolute(絕對定位)否(脫離排版、浮起來)相對於最近一層「非 static 的祖先元素」(祖先有 transform、filter 等屬性時也會成為基準)卡片右上角徽章、下拉選單、彈跳燈箱
fixed(固定定位)否(脫離排版、浮起來)相對於「瀏覽器視窗(Viewport)」(祖先有 transform、filter 等屬性時例外)固定置頂 Header、右下角客服與返回頂部按鈕
sticky(黏性定位)是(滾動前等同 relative)相對於最近一層「滾動容器」捲動到頂端時自動吸附的表格標題列、側邊目錄

1. relative(相對定位)與 absolute(絕對定位)的黃金口訣:「子絕父相」

初學者使用 position: absolute 最常遇到的問題就是:「為什麼我設了 top: 0; right: 0;,元素卻一路飛到整個網頁的最右上角?」

這是因為 position: absolute 會一層一層往上找父元素,直到找到第一個有設定定位(也就是 position 不是預設 static)的祖先元素,以它的 padding 邊框範圍作為對齊邊界;如果一路往上找都沒人設定,它就會改以「初始包含區塊」(initial containing block,大小等同第一個畫面的 Viewport,位於頁面最上方)為基準,看起來就是貼齊整個網頁的左上或右上角。

另外要留意:祖先元素即使維持 static,只要設定了 transform、perspective、filter(none 以外的值)或 contain: paint 等屬性,同樣會成為 absolute 子元素的定位基準。

因此實務切版有一句黃金口訣——「子絕父相」:

  • 內層要浮動對齊的子元素設為 position: absolute;
  • 外層作為邊界基準的父容器設為 position: relative;
/* 父層卡片:加上 relative,把自己變成子元素的座標原點 */
.product-card {
  position: relative;
  width: 280px;
  padding: 16px;
}

/* 子層「熱銷」標籤:加上 absolute,精準貼在卡片右上角 */
.hot-badge {
  position: absolute;
  top: 12px;
  right: 12px;
  background-color: #ef4444;
  color: #ffffff;
  padding: 4px 8px;
}

2. fixed(固定定位):永遠停留在瀏覽器視窗的某個角落

設定為 position: fixed 的元素會完全脫離頁面排版,直接固定在瀏覽器可視視窗(Viewport)的指定座標上。無論使用者怎麼上下捲動網頁,它都紋絲不動:

/* 右下角固定「回到頂端」按鈕 */
.back-to-top-btn {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 50; /* 確保按鈕浮在內文上方 */
}

注意事項:當任一層祖先元素設有 transform、perspective、filter 或 backdrop-filter(none 以外的值)、contain: paint(或 layout、strict、content),或 will-change: transform 時,該祖先元素會變成 fixed 子元素的定位基準,導致 fixed 看起來像變成 absolute 一樣跟著捲動。因此全站燈箱(Modal)或固定按鈕建議直接放在 <body> 第一層。


3. sticky(黏性定位):結合 relative 與 fixed 的滾動吸附神器

position: sticky 在使用者還沒滑到它之前,表現就跟一般 relative 元素一樣正常排列;一旦畫面往下捲動,讓它碰到你設定的 top 距離(例如 top: 0 或 top: 80px)時,它就會像 fixed 一樣吸附在畫面上,直到它的父容器底部捲上來、把它一起帶走為止:

/* 文章右側目錄:滑動到距離視窗頂部 24px 時自動停住吸附 */
.article-toc {
  position: sticky;
  top: 24px;
  align-self: flex-start; /* 若在 Flex 容器內,必加這行防止高度被拉伸為 100% */
}

常見問題:為什麼我的 position: sticky 完全沒效果?

如果你寫了 position: sticky 卻發現元素還是直接被捲走,通常是踩到以下 3 個原因之一:

  1. 忘記寫 top(或 bottom / left / right)偏移值:只寫 position: sticky 而四個偏移值都是 auto 時,元素的表現就跟 relative 一樣,不會觸發吸附,必須同時指定觸發距離,例如 top: 0;。
  2. 外層某一個祖先容器設定了 overflow: hidden、overflow: auto 或 overflow: scroll:這是最多人卡關的原因!overflow 設為 hidden、auto 或 scroll 的祖先都會成為滾動容器,sticky 改以這個容器的捲動作為吸附依據;這個容器本身沒在捲動時,sticky 就無法感知視窗的捲動。請將外層的 overflow: hidden 移除,或改用 overflow: clip。
  3. 元素的高度已經跟父容器一樣高(完全沒有可滑動的軌道空間):例如在 display: flex 雙欄排版中,左右兩欄預設會被 align-items: stretch 拉成一樣高。只要替 sticky 那一欄加上 align-self: flex-start;,讓它縮回自身內容高度,就能正常吸附了。

若固定定位元素上需要搭配半透明毛玻璃背景模糊效果(backdrop-filter),請進一步閱讀 CSS Filters 濾鏡效果。


參考資料與延伸閱讀

發佈留言