Wordpress

修正 Elementor 導覽選單那一下「閃」:實測穩定解法分享

在使用 Elementor 的導覽選單製作網站頁首(Header)時,常會遇到一個令人在意的細節:網頁剛載入時,選單項目會突然「跳動」一下,然後才看到下拉箭頭出現。

這種畫面上的抖動不只是視覺干擾,它其實會讓使用者在第一眼就感覺到畫面的不穩定與不一致。雖然它不構成錯誤,也不會破壞功能,但在體驗上卻足以讓整體介面顯得不夠扎實。

特別是當網站啟用了像 WP Rocket、LiteSpeed Cache 等快取外掛時,問題會變得更加明顯。這些外掛通常會調整 JavaScript 的載入時機,或延後腳本執行,進而放大了這種「畫面先顯示、箭頭後補上」的時間差。

問題根源:下拉箭頭是由 JS 動態插入

Elementor 的 Nav Menu 元件,在前端初始化時會掃描所有有子選單的項目,然後用 JavaScript 動態插入箭頭圖示。這個插入動作通常是在網頁主體載入完成後進行,也就是說——HTML 一開始是不含這些箭頭的。

具體來說,Elementor 是在 element_ready 階段插入 <span class="sub-arrow">,這動作不大,但正好會觸發瀏覽器對 DOM 結構的重新排版(reflow)與重繪(repaint)。因此你會看到畫面明明已經排好,卻因為多了一個元素導致整列選單發生閃爍或跳位現象。

這種「先載入再插入」的策略也會與 CSS 的排版邏輯產生衝突,尤其你若使用的是 flexbox、inline-block 排版方式,或設計上使用了自訂的間距、字級、行高等控制,這個箭頭插入的延遲就會非常明顯,甚至影響整體版面穩定性。

解決方向:讓箭頭預先存在、並隱藏插入的箭頭

既然問題在於JS插入的箭頭出現得太晚,我們就反過來處理:讓箭頭隱藏起來。

更精確的說法是:

  1. 讓 Elementor 的箭頭在伺服器端(PHP)渲染階段就寫入 HTML。
  2. 同時隱藏JS插入的箭頭,讓視覺上不會因為晚插入的箭頭產生偏移閃爍。

這樣做可以確保兩件事:

  • 畫面第一次載入時,箭頭就已經在 DOM 結構中,就算因為新增節點而重繪,也早一先一步使用css將其隱藏。
  • Elementor 的其他功能(例如子選單展開收合)依然可以正常使用,因為我們只是隱藏了原本閃爍的箭頭,並沒有移除整體互動行為。

程式碼實作

這段程式碼可以直接放進主題的 functions.php,或打包成自訂外掛使用。

不過更建議的做法是,建立一個子佈景主題(Child Theme),將這類客製化功能集中管理。這樣不僅能讓代碼結構更清晰,也能避免日後主題更新時自訂修改被覆蓋或消失的情況。

/**
 * 修復 Elementor 導航選單(Nav Menu)下拉指標閃爍問題
 * ------------------------------------------------------------
 * - 攔截 Nav Menu widget 的 HTML 輸出
 * - 插入並修正子選單箭頭圖示位置與樣式
 * - 修正 SVG 顏色繼承、避免閃爍或重疊
 * - 僅前台生效(不影響後台與 Elementor 編輯器)
 */

add_filter('elementor/widget/render_content', function ($content, $widget) {
    if (is_admin() || (defined('ELEMENTOR_EDITOR') && ELEMENTOR_EDITOR)) {
        return $content;
    }
    if (!did_action('elementor/loaded')) {
        return $content;
    }
    if (!class_exists('\Elementor\Icons_Manager')) {
        return $content;
    }
    if ('nav-menu' !== $widget->get_name()) {
        return $content;
    }
    if (strpos($content, 'menu-item-has-children') === false) {
        return $content;
    }

    $icon = $widget->get_settings_for_display('submenu_icon');
    $icon_html = '';

    if (!empty($icon)) {
        if (is_array($icon)) {
            ob_start();
            \Elementor\Icons_Manager::render_icon($icon, ['aria-hidden' => 'true']);
            $rendered = ob_get_clean();

            $rendered = preg_replace('/fill="[^"]*"/i', '', $rendered);
            $rendered = preg_replace('/<svg(.*?)>/i', '<svg$1 fill="currentColor">', $rendered);

            $icon_html = '<span class="sub-arrow sub-arrow-icon">' . $rendered . '</span>';
        } elseif (is_string($icon)) {
            $icon_html = '<span class="sub-arrow sub-arrow-icon"><i class="' . esc_attr($icon) . '"></i></span>';
        }
    }

    if ($icon_html) {
        $content = preg_replace(
            '#(<li[^>]*class="[^"]*\bmenu-item-has-children\b[^"]*"[^>]*>\s*<a[^>]*?>)(.*?)(</a>)#si',
            '$1$2 ' . $icon_html . '$3',
            $content
        );
    }

    static $css_printed = false;
    if (!$css_printed) {
        add_action('wp_footer', function () {
            echo '<style id="pongo-navmenu-icon-css">
                .elementor-nav-menu .sub-arrow { display: none !important; }
                .elementor-nav-menu .sub-arrow.sub-arrow-icon {
                    display: inline-block !important;
                    vertical-align: middle;
                    color: inherit !important;
                    fill: currentColor !important;
                }
                .elementor-nav-menu .menu-item-has-children:hover .sub-arrow-icon,
                .elementor-nav-menu .menu-item-has-children:focus-within .sub-arrow-icon {
                    color: currentColor !important;
                    fill: currentColor !important;
                }
            </style>';
        });
        $css_printed = true;
    }

    return $content;
}, 10, 2);Code language: PHP (php)

完成上述修改後,網頁載入的穩定性會有明顯改善。最直接的體感是:導覽選單不再抖動,箭頭從一開始就存在於版面中,整體畫面不再因 DOM 被修改而重新排版,也不會因箭頭晚一步出現而產生錯位、重疊、或 hover 效果異常的問題。

此外,這個方法是使用 Elementor 官方所提供的過濾器來增加輸出一個箭頭,不修改 Elementor 核心、不破壞編輯器預覽,對未來的更新也保有良好相容性。

結語

Elementor 之所以選擇在載入後再插入箭頭圖示,是出於元件化架構與樣式彈性的考量。然而在實際運行中,這種設計卻為導覽列帶來了意料之外的副作用——畫面閃爍、位移、整體穩定性下降。

如果你也遇到這個情況,其實不用再懷疑是 CSS 排版出了問題,也不必為了避開它而關閉快取或調整 JS 延遲策略。只要幾行簡潔的程式,就能讓導覽列在載入時維持穩定、不跳、不抖,呈現出應有的設計品質與細節一致性。

常見問答

本區問答由 AI 依文章內容自動整理,僅供快速參考,正式內容仍以全文為準。

為什麼選單箭頭會比畫面慢一步出現?

這是因為 Elementor 的下拉箭頭不是隨網頁主體一起輸出,而是等頁面載入完成後才用 JavaScript 動態插入。若網站又啟用 WP Rocket、LiteSpeed Cache 等快取外掛,這些外掛會延後腳本執行,時間差會更明顯。

Elementor 在哪個階段插入下拉箭頭圖示?

Elementor 是在前端初始化的 element_ready 階段,用 JavaScript 動態插入箭頭元素。這個動作雖然不大,卻會觸發瀏覽器對 DOM 結構的重新排版與重繪,讓原本排好的選單畫面閃爍或跳位。

要如何避免 Elementor 選單箭頭出現閃爍?

作法是讓箭頭改在伺服器端的 PHP 渲染階段直接寫入 HTML,同時用 CSS 隱藏原本由 JavaScript 插入的箭頭。這樣箭頭在畫面第一次載入時就存在,不會因為晚一步插入而產生偏移或閃爍。

修復用的程式碼建議放在哪裡管理?

這段程式碼可以直接放進主題的 functions.php,或包成自訂外掛使用。不過更建議建立子佈景主題來集中管理,這樣程式結構比較清晰,也能避免日後主題更新時,自訂修改被覆蓋或消失。

這個修復方式會影響 Elementor 編輯器嗎?

不會。這個方法是透過 Elementor 提供的過濾器輸出箭頭圖示,沒有修改 Elementor 核心程式,也不會破壞編輯器預覽功能,對日後版本更新也保有良好相容性。