Are you a GenerateCustomer?

Do you have an active GP Premium or GenerateBlocks Pro license key?

Create a GenerateSupport account for GeneratePress and GenerateBlocks support in one dedicated place.

Create an account
Already have a GenerateSupport account? Login

Just browsing?

Feel free to browse the forums. Support for our free versions is provided on WordPress.org (GeneratePress, GenerateBlocks).

Want to become a premium user? Learn more below.

smooth-scroll does not work with menu items due to where the class is added

  • Using a premium template.

    The menu is generated using <li> items. When you apply the smooth-scroll class to each menu items GP Premium puts the class against the <li> item in the menu and not the <a> link as it should.

    E.g. you add the smooth-scroll to the CSS Classes field in the menu, save (having activated smooth-scroll in the customiser). The class is added to the LI item and not the A item as in:

    
    <li id="menu-item-48762" class="smooth-scroll menu-item menu-item-type-custom menu-item-object-custom menu-item-48762"><a href="#our-services">Services</a></li>
    

    As you can see the class has been added to the menu list item and NOT the <a> link – hence smooth-scroll does not work?

    I do have some javascript code that fixes all this and adds an appropriate scroll delay (adjustable) and does NOT rely on settings in the customiser to make stuff “work”. I just wondered if this is a known problem and if there is a native GP Pro or GB Pro solution?

  • Hi there,

    Try this CSS instead of GP’s smooth-scroll function.

    html {
        scroll-behavior: smooth;
    }
  • Thanks for the reply. The issue is that it does not allow for selective anchors and delay times (e.g. say 1.2 seconds for smooth scroll). I find the GP solution ‘Kludgy’ (at best) and perhaps an after thought. My solution which works taking into account menu bars and offsets plus debug code to testing. In case anyone is interested LOL

    
    /*
      Smooth hash-link scroll (1200 ms)
      - Works for any same-page <a href="#section"> link
      - Offsets fixed/sticky headers you list in HEADER_SELECTORS
      - Disables CSS smooth scrolling during the animation so timing is consistent
      - Moves focus to the target for accessibility
      Drop this before </body>. Ensure your CSS does not set <code>html,body { scroll-behavior: smooth }</code>
      
      Ensure browser CSS smooth scrolling does not override JS timing (add this to your custom CSS header)
        html, body { scroll-behavior: auto; }
    */
    (function () {
      // --- Config ---------------------------------------------------------------
    
      var DEBUG = false;              // set to true to see console logs while testing
      var DURATION = 1200;            // milliseconds
      var HEADER_SELECTORS = [        // add or remove selectors as needed
        '#wpadminbar',
        '#site-navigation',
        '#sticky-navigation',
        '.site-header',
        '.main-navigation'
      ];
    
      // --- Helpers --------------------------------------------------------------
    
      function log () {
        if (DEBUG && window.console) {
          console.log.apply(console, ['[smooth]'].concat([].slice.call(arguments)));
        }
      }
    
      // Sum the heights of any fixed or sticky headers so the target sits below them
      function getOffset () {
        var h = 0;
        HEADER_SELECTORS.forEach(function (sel) {
          document.querySelectorAll(sel).forEach(function (el) {
            var pos = getComputedStyle(el).position;
            if (pos === 'fixed' || pos === 'sticky') h += el.offsetHeight;
          });
        });
        return h;
      }
    
      // Find the element that matches the hash (by id, or legacy name=)
      function findTarget (hash) {
        if (!hash || hash === '#') return null;
        var id = decodeURIComponent(hash.slice(1));
        return document.getElementById(id) || (document.getElementsByName(id)[0] || null);
      }
    
      // Ease-in-out for a pleasant scroll
      function easeInOut (t) {
        return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
      }
    
      // Core animation: scroll to targetY over duration
      function smoothScrollTo (targetY, duration) {
        // Choose the root scrolling element for cross-browser consistency
        var root = document.scrollingElement || document.documentElement;
    
        // Temporarily disable CSS smooth behaviour so our JS timing is used
        var prevHtml = document.documentElement.style.scrollBehavior || '';
        var prevBody = document.body.style.scrollBehavior || '';
        document.documentElement.style.scrollBehavior = 'auto';
        document.body.style.scrollBehavior = 'auto';
    
        if (duration <= 0) {
          root.scrollTop = targetY;
          window.scrollTo(0, targetY); // Safari quirk safety
          // Restore prior styles
          document.documentElement.style.scrollBehavior = prevHtml;
          document.body.style.scrollBehavior = prevBody;
          return Promise.resolve();
        }
    
        var start = root.scrollTop;
        var dist = targetY - start;
        var t0 = performance.now();
    
        return new Promise(function (resolve) {
          function step (now) {
            var t = Math.min((now - t0) / duration, 1);
            var y = Math.round(start + dist * easeInOut(t));
            root.scrollTop = y;
            window.scrollTo(0, y); // help WebKit-based browsers
            if (t < 1) {
              requestAnimationFrame(step);
            } else {
              // Restore prior styles
              document.documentElement.style.scrollBehavior = prevHtml;
              document.body.style.scrollBehavior = prevBody;
              resolve();
            }
          }
          requestAnimationFrame(step);
        });
      }
    
      // --- Click handling -------------------------------------------------------
    
      // Capture-phase listener so we still run if other handlers stop propagation
      document.addEventListener('click', function (e) {
        var a = e.target.closest && e.target.closest('a');
        if (!a) return;
    
        // Let modified clicks behave normally
        if (e.defaultPrevented || e.button !== 0 || a.target === '_blank' ||
            e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
    
        // Ignore bare "#" links (often used as JS triggers)
        var href = a.getAttribute('href') || '';
        if (href === '#') return;
    
        var hash = a.hash || '';
        if (!hash || hash === '#') return;
    
        // Only run if the target exists on this page
        var target = findTarget(hash);
        if (!target) return;
    
        log('click ->', hash, 'target found:', target);
    
        e.preventDefault();
    
        var offset = getOffset();
        var targetY = Math.max(0, target.getBoundingClientRect().top + window.pageYOffset - offset);
    
        smoothScrollTo(targetY, DURATION).then(function () {
          // Keep the hash in the URL without a jump
          try { history.pushState(null, '', hash); } catch (err) {}
    
          // Move focus to the target for accessibility
          target.setAttribute('tabindex', '-1');
          target.focus({ preventScroll: true });
          // Optional tidy: remove the temporary tabindex after focus shifts away
          target.addEventListener('blur', function cleanup () {
            target.removeAttribute('tabindex');
            target.removeEventListener('blur', cleanup);
          });
    
          log('scrolled to', hash);
        });
      }, true);
    
      // --- On-load behaviour ----------------------------------------------------
    
      // If the page loads with a hash, scroll to it smoothly as well
      window.addEventListener('load', function () {
        if (location.hash.length > 1) {
          var target = findTarget(location.hash);
          if (!target) return;
          var offset = getOffset();
          var y = Math.max(0, target.getBoundingClientRect().top + window.pageYOffset - offset);
          log('onload ->', location.hash);
          smoothScrollTo(y, DURATION);
        }
      });
    })();
    
  • Thanks for sharing your solution 🙂

    However, GP smooth scroll does have filter to change the scroll speed https://docs.generatepress.com/article/generate_smooth_scroll_duration/

    And it can also include all <a> elements with anchor links.
    https://docs.generatepress.com/article/generate_smooth_scroll_elements/

    Just thought using CSS is much easier.

  • Yes I know. But as per my original comment, the templates that are available with GP Pro -> Menu settings use <li> items. When you add the “smooth-scroll” CSS class to the links in the menu items it add the class to the <li> and not the <a> link – which it needs to enact smooth scrolling.

    The second link you provide is an ‘ok’ solution but not flexible with menu headers, offsets for various elements and time delays – settable in the same piece of code and as I say – somewhat messy having to dart around all over the place when all you want is a delay time, offsets calculated and all # links automatically scrolling smoothly.

    Thanks for getting back

  • You are welcome   🙂

Viewing 6 posts - 1 through 6 (of 6 total)
  • You must be logged in to reply to this topic.