新視野行銷企劃

內部錨點與固定導覽列的互動設計

扁平化風格的網頁設計插圖,主題為內部錨點與固定導覽列的互動設計,畫面呈現錨點符號與固定導覽列的結構,象徵頁面滾動與定位的設計概念,搭配現代配色與繁體中文標題,展現專業與清晰的網頁互動設計主題。

背景與概念說明

相信不少前端開發者都有過這樣的經驗:點擊頁面內的錨點連結(anchor link)後,頁面瞬間跳轉到目標區塊,但發現該區塊的標題被頂部的固定導覽列遮住了!這種情況令使用者無法看到段落開頭的內容,體驗很不友善。為什麼會這樣呢?主要原因在於固定導覽列(fixed navigation bar)在頁面頂部佔據空間,而瀏覽器預設在跳轉錨點時,會把目標元素頂到視窗最上方,結果就被導覽列蓋住了。這就是所謂的「錨點偏移」問題:錨點定位的內容相對於視窗的位置產生了偏差。

在現代網頁中,固定導覽列相當常見,因為它可以讓使用者在捲動頁面時始終看到導航選單,快速跳轉到各部分內容。然而,搭配錨點連結時,就必須解決Anchor Offset問題,確保錨點目標不會被導覽列遮蓋。此外,我們也常希望增加一些互動效果,例如平滑捲動(smooth scrolling)讓畫面過渡更順暢,以及捲動偵測高亮(scroll spy)在使用者瀏覽長頁面時自動高亮目前所在章節對應的導覽項目,提升使用者定位當前內容的體驗。

本教學將針對具有一定前端基礎的開發者,逐步說明如何使用 HTML、CSS 和純 JavaScript,實作「內部錨點搭配固定導覽列」的最佳效果。我們將從基礎頁面架構出發,逐一實現:避免錨點被固定導覽列遮擋、平滑捲動效果,以及捲動時導覽項目的自動高亮。整篇文章以原生技術為主,不依賴任何框架或插件,讓你深入理解其中的原理。準備好了嗎?讓我們開始吧!

實作步驟

步驟 1:建立基本的 HTML 架構

首先,我們建立一個簡單的 HTML 網頁結構,其中包含固定導覽列和多個內容區塊。導覽列會有若干連結(<a>),對應到頁面內部的錨點。每個錨點區塊這裡用<section>來表示,並給予唯一的 id 供錨點連結定位。

下面是範例結構:

HTML 基本架構
<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>錨點與固定導覽列範例</title>
</head>
<body>
  <!-- 固定導覽列 -->
  <nav>
    <a href="#section1">Section 1</a>
    <a href="#section2">Section 2</a>
    <a href="#section3">Section 3</a>
  </nav>

  <!-- 內容區塊們(錨點目標) -->
  <section id="section1">
    <h2>第一部分</h2>
    <p>這是第一部分的內容...</p>
  </section>
  <section id="section2">
    <h2>第二部分</h2>
    <p>這是第二部分的內容...</p>
  </section>
  <section id="section3">
    <h2>第三部分</h2>
    <p>這是第三部分的內容...</p>
  </section>
</body>
</html>

上述 HTML 中,我們建立了一個簡單的導航列 <nav>,其中包含三個錨點連結,它們的 href 對應於頁面中三個 <section> 區塊的 id(section1、section2、section3)。這樣一來,點擊導覽列的連結時,瀏覽器會自動跳轉到相應的 <section>。接下來,我們將為導覽列加入 CSS,讓它固定在頁面頂部,同時確保內容不被遮擋。

步驟 2:設定固定導覽列的基本樣式 (CSS)

現在,我們使用 CSS 將導覽列固定在頁面頂端,並設計其外觀。固定導覽列通常使用 position: fixed 或 position: sticky 實現。這裡我們選擇 position: fixed,使導覽列在頁面上始終停留在視窗頂部。為了讓後續內容不被導覽列壓住,我們也會對 <body> 或內容容器加入上方內間距(padding)。

以下是基本的 CSS 設定:

CSS 固定導覽列樣式
/* 讓整體盒模型計算更直觀 */
* {
  box-sizing: border-box;
}

/* 基本頁面樣式調整 */
body {
  margin: 0;
  padding-top: 60px; /* 預留空間,避免內容被高度60px的導覽列遮住 */
  font-family: sans-serif;
}

/* 導覽列樣式 */
nav {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 60px;
  background-color: #333;
  color: #fff;
  display: flex;
  align-items: center;
  padding: 0 20px;
  z-index: 1000; /* 確保導覽列蓋在其他內容上方 */
}

/* 導覽列中的連結樣式 */
nav a {
  color: #fff;
  text-decoration: none;
  margin-right: 15px;
  font-size: 1rem;
}

/* 可選:導覽列連結的hover效果 */
nav a:hover {
  text-decoration: underline;
}

在這段 CSS 中,我們做了以下幾件事:

對 body 設置了 padding-top: 60px,這個數值等於導覽列的高度,確保頁面內容從導覽列下方開始,而不會被導覽列覆蓋。這對於頁面載入初始位置非常重要。若不這樣做,第一個 <section> 會被固定在頂部的導覽列擋住一部分。

使用 nav { position: fixed; top: 0; width: 100%; } 將導覽列固定定位在視窗頂部,並撐滿寬度。

設定了一些外觀樣式,例如背景色 #333(深灰)、文字顏色白色,以及 display: flex 將連結橫向排列置中對齊。

z-index: 1000 非常重要,讓導覽列的堆疊位在最前面,避免在頁面上出現其他內容或元件遮蓋導航列的情況。

到此,固定導覽列已經可以正常工作了。你可以試著打開網頁,滾動看看:導覽列會一直停留在頂部不動。然而,如果此時點擊導覽列的錨點連結,你可能會遇到我們開頭提到的問題:跳轉後的區塊標題依然被導覽列遮住。下一步我們就來解決錨點偏移的問題。

步驟 3:解決錨點被固定導覽列遮擋(錨點偏移修正)

一般來說,當使用 <a href="#section1"> 這種錨點連結時,瀏覽器會讓 id="section1" 的元素頂格出現在視窗頂部。由於我們的導覽列固定在頂部,目標元素就會剛好跑到導覽列背後,導致內容遮擋。為了解決這個錨點偏移問題,我們可以利用 CSS 提供的解決方案。

最新的 CSS 中有兩個屬性可以用來調整滾動定位的偏移:

scroll-padding-top: 針對滾動容器(例如 html 或特定容器),指定在滾動定位時容器頂部預留的內間距。

scroll-margin-top: 針對目標元素,指定在滾動對齊時元素頂部預留的外間距。

這兩種方法效果相似,選一種用即可。我們這裡採用 scroll-margin-top,直接在錨點目標元素上設定向下偏移的距離。由於我們錨點目標是每個 <section>,而導覽列高度為 60px,所以我們讓每個 section 在滾動進視窗時預留 60px 的空間:

CSS 錨點偏移修正
/* 避免錨點跳轉內容被固定導覽列遮住 */
section {
  scroll-margin-top: 60px;
}

將上述 CSS 新增到樣式表中。這麼做之後,當使用者點擊導覽列的錨點連結,瀏覽器在滾動目標 <section> 進入視窗時,會自動在其頂端留出60px的距離,相當於把目標內容往下推了60px,正好避開高度為60px的導覽列。換句話說,scroll-margin-top 幫我們自動預留空白,確保錨點元素不會被固定導覽列擋住。

小提示: 你也可以使用 html { scroll-padding-top: 60px; } 達成類似效果,作用是在整個頁面的滾動環境預留頂部空間。兩者的差別在於實作層面略有不同,但對最終用戶效果幾乎一樣。scroll-margin-top 是設在目標元素上,每個錨點元素都可各自設定;scroll-padding-top 則是設在滾動容器上(通常是 html, body),統一為所有錨點提供偏移量。依據你的需求選擇其一即可。

現在再試著點擊錨點連結,應該可以看到,被導覽列蓋住標題的問題消失了——每個標題上方都有適當的空間,看起來很舒服。這解決了導覽列遮擋錨點的煩人問題。

步驟 4:加入平滑捲動效果

目前我們的錨點跳轉是瞬間跳到目標區域。雖然功能上沒問題,但體驗上稍嫌突兀。接下來,我們要實現平滑捲動效果:當點擊錨點連結時,頁面會以動畫方式順暢地捲動到指定位置,而不是瞬間跳轉。這種視覺上的平滑過渡能讓使用者更容易跟蹤視線,不會突然迷失在新位置。

實作平滑捲動有兩種途徑:

CSS 方法:使用 scroll-behavior: smooth

JS 方法:攔截錨點點擊事件,用 window.scrollTo 或 element.scrollIntoView 等帶平滑選項的 API 來執行滾動。

最簡單的莫過於 CSS 一行設定。在我們的範例中,只需對 HTML 設定全域的平滑滾動行為:

CSS 平滑捲動
/* 全域啟用平滑捲動 */
html {
  scroll-behavior: smooth;
}

把這行加到你的 CSS 裡。現在,所有透過錨點連結觸發的滾動(以及調用瀏覽器內建滾動 API 的行為)都會變成平滑動畫效果了。不需要任何額外的 JavaScript!此時再點擊一次導覽列中的「Section 2」或「Section 3」,你會看到頁面內容柔和地滑動到新位置,而非突然跳轉,體驗提升不少。

注意: scroll-behavior: smooth; 已被大多數現代瀏覽器支援,但如果你需要支援不支援此屬性的環境(例如較舊版的 Safari 或 IE 瀏覽器),可以考慮加上一段 JavaScript 作為後備方案。在本文中我們以 CSS 方法為主;若有需要,稍後的問與答部分會提及 JavaScript 平滑滾動的替代方案。

步驟 5:實作滾動時導覽項目高亮 (Scroll Spy)

最後一個功能,是在使用者滾動瀏覽頁面時,自動高亮導覽列中對應當前所在區塊的連結。這個效果常被稱作 Scroll Spy(捲動侦測)——很多前端框架(如 Bootstrap)的元件都有類似功能,但我們可以用原生 JavaScript 自己實現。

實作的思路如下:偵聽頁面的捲動事件,每當使用者捲動時,程式計算目前哪一個 <section> 正位於視窗中(或接近頂部的位置)。然後,根據該 section 的 id,找到導覽列中對應的 <a>,將其標示為「active」(例如加上一個特殊的 class)。同時,要移除其他連結的高亮狀態。如此即可做到隨捲動自動切換導覽高亮。

讓我們來實現它。首先,在 CSS 中定義一個樣式供高亮顯示使用,例如 .active 狀態下改變文字顏色或其他效果:

CSS 導覽高亮樣式
/* 高亮當前區塊對應的導覽項目 */
nav a.active {
  font-weight: bold;
  text-decoration: underline;
}

以上設定當連結具有 active 類別時,加粗字體並加底線,以醒目顯示。目前 .active 還不會自動套用,我們接下來用 JS 讓它動起來。

加入以下 JavaScript 腳本(可以放在頁面底部,緊貼 </body> 前,或是用 <script> 引入外部檔案):

JavaScript Scroll Spy 實作
<script>
  // 取得所有的 section 和 nav 中的連結
  const sections = document.querySelectorAll('section');
  const navLinks = document.querySelectorAll('nav a');

  window.addEventListener('scroll', () => {
    let currentSectionId = "";

    // 找出目前視窗所在的區塊(以頁面頂端位置判斷)
    sections.forEach(section => {
      const sectionTop = section.offsetTop - document.querySelector('nav').offsetHeight;
      const sectionHeight = section.offsetHeight;
      if (window.scrollY >= sectionTop && window.scrollY < sectionTop + sectionHeight) {
        currentSectionId = section.getAttribute('id');
      }
    });

    // 根據 currentSectionId,切換導覽列中對應連結的 active 狀態
    navLinks.forEach(link => {
      const targetId = link.getAttribute('href').substring(1); // 去掉開頭的 '#'
      if (targetId === currentSectionId) {
        link.classList.add('active');
      } else {
        link.classList.remove('active');
      }
    });
  });
</script>

這段程式碼做了以下事情:

用 querySelectorAll 抓取頁面中的所有 <section> 以及所有 <nav> 裡的 <a> 連結,分別存入 sections 和 navLinks 變數。

監聽 window 的 scroll 事件,每當頁面發生捲動時就執行回呼函式。

在每次捲動時,迴圈遍歷所有區塊 sections,利用 section.offsetTop 和 section.offsetHeight 計算區塊的位置範圍。如果目前的 scrollY(頁面頂部離開文件頂端的距離)落在某個 section 的範圍內,表示使用者視窗此刻正看到該區段,我們將記錄下這個區段的 ID。

我們從每個 section 的 offsetTop 減去導覽列的高度來得到實際出現在視窗頂部時的臨界位置,這樣可以更準確地對齊可視範圍。(這裡用 document.querySelector('nav').offsetHeight 動態取得導航列高度。若之前有直接使用固定值60px,也可以在此寫死 -60。動態取得則比較萬一導覽列高度調整也能自適應。)

找出當前所在的 section 之後,再遍歷每一個導覽連結 navLinks。把所有連結的 .active 類先移除,然後比對每個連結的 href 是否對應我們偵測到的 currentSectionId。如果匹配,則加上 .active 類別。如此即可更新高亮狀態。

現在,大功告成!當你瀏覽頁面、向下滾動時,對應所在區塊的導航連結會自動加上粗體底線標示。比如當你滾動進入 Section 2 區域時,「Section 2」這個連結會高亮顯示,讓使用者清楚知道目前閱讀的位置。在長篇幅的一頁式網站或文件頁面中,這種 Scroll Spy 功能非常實用。

進階提示: 上述用 scroll 事件監聽的方法在絕大多數情況下都表現良好。但如果你的頁面內容非常多、區塊非常多,頻繁計算可能帶來性能負擔。此時可以考慮使用 Intersection Observer API 來更有效率地監測元素的可見性,取代手動計算 scrollY。另外,別忘了節流(throttle)或防抖(debounce)技巧也能避免滾動事件觸發過於頻繁導致卡頓。

經過以上五個步驟,我們已經實現了一個功能完整的頁面範例:具有固定頂端導航列、內部錨點連結正確對齊(不會被遮擋)、點擊連結平滑捲動過渡、以及滾動時自動更新導覽高亮。接下來,我們會整理一些常見的問題和解決方案,幫助你在實作類似功能時避免陷入那些小陷阱。

問與答 (Q&A)

錨點連結跳轉後的位置還是有點不對,或直接透過 URL hash(如 example.com/page#section2)進入頁面時,內容依然被遮住怎麼辦?
首先,確認已經正確設定了前述的 scroll-margin-top 或 scroll-padding-top。大部分情況下,這就能解決錨點偏移問題,無論是點擊跳轉還是直接帶 #hash 開啟頁面。若你需要支援更舊的瀏覽器(例如不支援上述 CSS 屬性的情形),可以用 JavaScript 來微調:在頁面載入完成或 hash 變化時,檢查 window.location.hash,找到對應元素,使用 window.scrollBy() 手動再向下滾動一點位移。例如:
JavaScript 後備方案
window.addEventListener('load', () => {
  if (window.location.hash) {
    window.scrollBy(0, -document.querySelector('nav').offsetHeight);
  }
});

這段程式會在頁面載入時,若發現 URL 中有 hash,就將頁面再往上拉一段(導覽列高度)距離,確保對應內容露出。總之,CSS 解決方案較為優雅,而 JS 後補方案可作為輔助,用於特殊情況下調整。

固定導覽列的 z-index 該如何設定?有時候我發現導覽列和其他元素會互相遮蓋,出現衝突。
z-index 控制元素的堆疊順序。固定導覽列通常應該蓋在大部分內容之上,所以我們在 CSS 中給 nav 設置了一個較大的 z-index(例如 1000)。這確保了普通內容不會蓋住導航列。不過,若你的頁面上還有其他固定或絕對定位的元素(例如彈出視窗、下拉選單等),也可能需要調整它們的 z-index 以配合。關鍵在於理解:只有在同一層級的定位上下文中,z-index 才可比較大小。所以如果兩個元素互相影響蓋蓋,檢查它們是否有共同的父容器及定位上下文。有需要時,可將導覽列的 HTML 結構放在頁面最底部,或至少確保其所在容器不被其他更高 z-index 的容器限制。同時,合理規劃各類元件的 z-index 層級,例如:導覽列1000、下拉選單1200、模態視窗2000等,避免衝突即可。
加了 scroll-behavior: smooth 之後在我的瀏覽器上沒有效果?平滑滾動在所有環境都支援嗎?
大多數現代瀏覽器(Chrome、Firefox、Edge、新版 Safari 等)都已支援 CSS 的平滑滾動。但某些較舊版本瀏覽器不支援,尤其是早期的 Safari 和完全不支援的 IE 瀏覽器。如果發現沒效果,很可能是瀏覽器不支援所致。在這種情況下,你可以改用 JavaScript 實現平滑滾動。例如,在點擊錨點連結時,取消瀏覽器預設行為,改用 element.scrollIntoView({ behavior: 'smooth' }) 或 window.scrollTo({ top: ..., behavior: 'smooth' }) 來執行滾動。這需要你寫一些額外的 JS 程式碼:抓取所有錨點連結,監聽它們的 click 事件,阻止預設跳轉,再執行平滑滾動。雖然稍嫌麻煩,但可以達到相容性的目的。幸運的是,CSS 平滑滾動已成為標準,除非需要照顧極舊的用戶群體,否則大可以依賴 CSS 解法。
Scroll Spy 功能在某些頁面上表現怪怪的,有時滾動過快時導覽高亮不同步,該怎麼優化?
滾動偵測高亮的實作需要考慮性能和正確性兩方面。若發現不同步或延遲,可能是捲動事件觸發頻率高導致計算跟不上,或者某些區塊高度過小導致快速掠過時來不及捕捉。優化的方法有幾種:1)節流/防抖:對 scroll 事件進行節流處理,降低觸發次數,使計算有足夠時間完成,同時不至於過度頻繁。2)調整判斷邏輯:例如可以設定一個稍微大的視窗區間(透過調整 sectionTop 判斷的偏移量或使用 Intersection Observer 的 threshold),讓區塊進入視窗中段時就算作「進入」激活狀態,避免臨界點錯過。3)使用 Intersection Observer API:它能更高效地通知你元素何時進入或離開視窗,可大幅減少手動計算和事件次數,對於長頁面非常有幫助。如果頁面很長、段落很多,建議嘗試 Intersection Observer 方案,它能精確控制觸發時機,並且瀏覽器會優化其效能,比傳統 scroll 事件更高效。

經過以上講解,你現在應該對「內部錨點與固定導覽列的互動設計」有了全面的瞭解。我們從定義問題出發,探討了固定導覽列如何影響錨點定位,並一步步實作了對應的解決方案和擴展功能。希望這篇教學能讓你在實際專案中輕鬆應對類似需求,打造出體驗佳、易於導航的一頁式網站或長頁面。祝你在前端開發的道路上玩得開心,寫出更出色的互動效果!

CONTACT US

網站設計報價洽詢

請填寫您的資料,我們將儘快與您聯繫! 為必填