點擊連結錨點 (anchor) 到定點時,卻被固定高度的導覽列遮住時該如何處理位移?

關於點擊連結錨點 (anchor) 到定點時,卻被固定高度的導覽列遮住時該如何處理位移?網路上有許多解法,例如使用 padding, margin 、偽元素、scroll-margin-top, position absolute + transform: translateY...這些都有可能會造成畫面破版。

經過研究後,最好的解法是透過 JavaScript 做處理:概念為對於所有錨點連結綁定點擊事件,在事件發生的同時計算錨點目標的元素位置 (top),最後再透過 jQuery 的 animate scroll 做滑順的捲動。

》備註:當中的 111 是你的固定導覽列的高度。

以下是範例程式碼:

window.jQuery(function () {

    window.jQuery('a[href^="#"]').click(function (e) {

      var hash = window.jQuery(this).prop("hash")
      if (hash && !hash.startsWith('#elementor-action')) {
        e.preventDefault();

          window.jQuery([document.documentElement, document.body]).animate({
              scrollTop: window.jQuery(hash).offset().top - 111
          }, 1000);

        return false;
      }
    })
})

如果你有使用 WordPress Elementor 的點擊 popup 功能,那麼你就會需要增加條件避免綁定到 elementor-action 的物件上。

這邊另外提供一個不需要 jQuery 的版本:

    scrollToFragment(fragment) {
      try {
        if (window) {
          // Retrieve the URL fragment
          if (!fragment) {
            fragment = window.location.hash
          }

          // Check if the fragment exists and is not empty
          if (fragment && fragment.length > 1) {
            // Remove the leading '#' character
            const target = fragment.substring(1)

            // Find the target element
            const targetElement = document.querySelector('#' + target)

            // Scroll to the target element if found
            if (targetElement) {
              const offset =
                targetElement.getBoundingClientRect().top + window.scrollY - 80

              window.scrollTo({
                top: offset,
                behavior: 'smooth',
              })
            }
          }
        }
      } catch (e) {
        // silent
      }
    },

我們有時還會想要優化當頁面載入時發現有 hash,那就會直接滾動到對應的位置以及如果直接在網址做錨點改變時也要滾動到對應的位置。整體的程式碼可以改寫為以下:

window.jQuery(function () {
    window.jQuery('a[href^="#"]').click(function (e) {
      var hash = window.jQuery(this).prop("hash")
      if (hash && !hash.startsWith('#elementor-action')) {
        e.preventDefault();
          smoothScrollToElementSelector(hash)
        return false;
      }
    })

    function smoothScrollToElementSelector(id) {
        window.jQuery([document.documentElement, document.body]).animate({
              scrollTop: window.jQuery(id).offset().top - jQuery('.app-header').height() - jQuery('#wpadminbar').height()
        }, 300);
    }

    if (window.location.hash) {
        smoothScrollToElementSelector(window.location.hash)
    }

    window.jQuery(window).on('hashchange', function () {
        smoothScrollToElementSelector(window.location.hash)
    })
})