滾動網頁切換 Header:用 JavaScript 實現導覽列下滑變換效果

在開發 WordPress 網站時,常會遇到「首屏導覽列」與「下滑後的導覽列」需要不同樣式的需求。例如在頁面頂部時,為了搭配大圖背景,Header 需要透明且高度較大;但當使用者向下捲動時,則希望出現一個固定在頂部、白底、高度較窄的精簡版導覽列。這種切換效果如果處理不當,容易造成兩個 Header 同時出現或切換瞬間版面跳動的問題。

運作原理說明

此實作方式是透過監聽瀏覽器的滾動事件(Scroll Event)來取得目前的垂直位移量(window.scrollY)。當位移量超過預設的門檻值(Threshold)時,JavaScript 會動態為原始 Header 加上隱藏標籤,同時為預備好的第二組 Header 加上顯示標籤。透過 CSS Class 的切換而非直接修改 Inline Style,能讓樣式控制回歸 CSS,後續維護樣式時也更具彈性。

實作步驟拆解

1. 定義 CSS 切換狀態

首先要在樣式表中定義好選單在切換前與切換後的狀態。我們預設將 .header-after 隱藏,並給予固定(fixed)在頂部的 CSS 屬性,確保它被觸發顯示時能正確黏在最上方。

<style>
.header-after {
    display: none !important;
}

.header-after.is-show {
    display: flex !important;
    align-items: center;
    width: 100vw !important;
    position: fixed;
    top: 0;
    left: 0;
    z-index: 9999;
}

.header-before.is-hide {
    visibility: hidden !important;
}
</style>

2. JavaScript 監聽與 Class 變換

這段腳本會監測捲動高度。當捲動超過 150px 時觸發狀態變更。我們使用了 passive 參數來優化滾動效能,避免腳本執行阻塞瀏覽器的原生滾動行為。

<script>
(function() {
    var done = false;
    
    function initHeader() {
        if (done) return;
        done = true;

        var headersBefore = document.querySelectorAll('.header-before');
        // 排除 Elementor 自動產生的佔位元素,避免抓錯對象
        var headerAfter = document.querySelector('.header-after:not(.elementor-sticky__spacer)');

        if (!headerAfter) return;

        var threshold = 150;
        var isShow = false;

        window.addEventListener('scroll', function() {
            var y = window.scrollY;

            if (!isShow && y > threshold) {
                isShow = true;
                headersBefore.forEach(function(el) {
                    el.classList.add('is-hide');
                });
                headerAfter.classList.add('is-show');
            } else if (isShow && y <= threshold) {
                isShow = false;
                headersBefore.forEach(function(el) {
                    el.classList.remove('is-hide');
                });
                headerAfter.classList.remove('is-show');
            }
        }, { passive: true });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initHeader);
    } else {
        initHeader();
    }
})();
</script>

注意事項與踩坑提醒

  • Elementor 相容性:如果你使用 Elementor Pro 的 Sticky 功能,系統會自動在 Header 外層包裹一個 .elementor-sticky__spacer。在選取元素時必須使用 :not(.elementor-sticky__spacer) 排除,否則 JavaScript 可能會操作到寬度為 0 的佔位元素,導致效果失效。
  • Z-index 圖層順序:.header-after 的 z-index 必須設定得足夠高(例如 9999),否則當網頁下方有使用到其他具有定位屬性的區塊(如 Slider 或 Map)時,導覽列可能會被遮擋。
  • Visibility 與 Display 的選擇:對於初始的 .header-before,建議使用 visibility: hidden 而不是 display: none。這樣可以保留原本 Header 佔據的空間高度,避免在切換瞬間頁面內容突然向上彈跳。
  • 效能考量:Scroll 事件觸發頻率極高,務必確保腳本邏輯精簡。使用一個 boolean 變數(如本例中的 isShow)來判斷狀態,可以避免在每次滾動時都不斷執行 classList.add,減少瀏覽器重繪的壓力。

看不懂沒關係,照做就能用

以上的原理看不懂沒關係,只要照著下面的步驟操作就能直接套用:

步驟一:設定 Elementor 容器類別

  1. 進入 Elementor 編輯你的 Header 範本。
  2. 點選「原本的 Header」容器,到「進階」分頁的「CSS Classes」欄位填入:header-before
  3. 點選「想要捲動後才出現的 Header」容器,到「進階」分頁的「CSS Classes」欄位填入:header-after

步驟二:加入 CSS 樣式

  1. 安裝並啟用 FluentSnippets 外掛。
  2. 新增一個 CSS 片段,將文章中的 CSS 程式碼貼入。
  3. 點擊儲存並啟用,最後清除網站快取。

步驟三:加入 JavaScript 腳本

  1. 在 FluentSnippets 新增一個片段。
  2. 將文章中的 JavaScript 程式碼貼入。
  3. 執行位置(Placement)選「Site Wide Footer」。
  4. 點擊儲存並啟用,清除快取後到前台查看效果。
如果啟用後網站出現異常,回到 Snippets 將該片段停用即可立刻復原,不會影響其他設定。
文章分享