Is Your Anchor Link Target Hidden Behind Divis’ Sticky Menu?
Editorial Note We may earn a commission when you visit links from this website.

When you use Divi by Elegant Themes you most likely use the “sticky menu” feature, which is enabled by default. This makes the top menu sticky when you scroll further down the page.

However, there’s one problem that frequently happens on projects I work on: When targeting a Link-Anchor, e.g. by using a <a href="#anchor"></a> link on the current page, Divi scrolls down too far. In fact, the Link-Anchor is always hidden behind the sticky menu, when the page can be scrolled far enough.

Table of Contents

The Drawbacks of Using A Plugin to Fix the Issue

There are a few examples on the web (like the one by Divi Booster) that work, but have some drawbacks:

  1. The script only fires once, during page load, but does not handle later clicks on anchor links that target the current site.
  2. When you want the page to scroll a little bit further down below the sticky menu, the code provides no solution. E.g. when you use a drop-shadow below the menu and want to keep an additional 20px space.
  3. Practices are “workarounds” but no clean solutions. E.g. setting “display: none” to prevent scrolling or using global scope variables inside JS closures.

The Code Solution

So, I wanted to create a modern solution that works well in 2023 and later. I started by adding an event handler for the hashchanged window event. So we do not depend on the DOM Loaded event (which only fires once per page) but can also intercept links that point to the current page.

Step 1

window.addEventListener('DOMContentLoaded', function(ev) {
 if (window.location.hash) {
  // On initial page load set the hash to empty and apply
  // the hash again after a short delay.
  var origHash = window.location.hash;
  window.location.hash = '';

  $(function() {
   // This will trigger the "hashchanged" event below.
   window.setTimeout(function() {
    window.location.hash = origHash;
   }, 600);
  });
 }
});

// This event does the actual work.
window.addEventListener('hashchange', function() { 
 console.log('Hash: ', window.location.hash)
});

In the second step, I built a new function scrollToAnchor(), which smoothly scrolls to the anchor element with the correct offset from the top. This function is called by the hashchanged event handler.

Step 2

function scrollToAnchor(ev) {
 // Use "preventDefault" and "return false" to bypass browsers default behavior.
 ev.preventDefault();

 if (!window.location.hash) {
  return false;
 }

 var anchorTarget = $(window.location.hash);

 if (!anchorTarget.length) {
  return false;
 }

 // Let the browser finish the current process, before scrolling up/down.
 $(function() {
  window.setTimeout(function(){
   var offset = parseInt(jQuery('html').css('margin-top')) +
    parseInt(jQuery('header').first().css('height')) +
    15; // <-- This adds a variable offset!
   var anchorTop = anchorTarget.offset().top;
   anchorTop -= offset;

   // Actually copied from Divis "frontend-builder-global-functions.js"
   $( 'html, body' ).animate({ scrollTop: anchorTop });
  }, 250);
 });

 return false;
}

The Full Code

Here is the full JS code that can be copied into a “divi-anchor.js” file in your child theme. I do not recommend copying and pasting it into the Divis option page but enqueue the JS file the correct way.

(function($) {
window.addEventListener('DOMContentLoaded', function(ev){
 if (window.location.hash) {
  var origHash = window.location.hash;
  window.location.hash = '';

  $(function() {
   window.setTimeout(function() {
    window.location.hash = origHash;
   }, 600);
  });
 }
});

window.addEventListener('hashchange', scrollToAnchor);

function scrollToAnchor(ev){
 ev.preventDefault();

 if (!window.location.hash) {
  return false;
 }

 var anchorTarget = $(window.location.hash);

 if (!anchorTarget.length) {
  return false;
 }

 // Let the browser finish the current process, before scrolling up/down.
 $(function() {
  window.setTimeout(function(){
   var offset = parseInt(jQuery('html').css('margin-top')) +
    parseInt(jQuery('header').first().css('height')) +
    15;
   var anchorTop = anchorTarget.offset().top;
   anchorTop -= offset;

   $( 'html, body' ).animate({ scrollTop: anchorTop });
  }, 250);
 });

 return false;
}
})(window.jQuery);

Simply enqueue this file in your themes functions.php, like this:

<?php

add_action( 'wp_enqueue_scripts', 'pst_theme_customize_scripts' );

/**
 * Enqueue custom JS and CSS files.
 */
function pst_theme_customize_scripts() {
 wp_enqueue_script(
  'child-theme-scroller',
  get_stylesheet_directory_uri() . '/divi-anchor.js',
  [ 'jquery' ],
  false,
  true
 );
}

Try Divi Areas Pro today

Sounds interesting? Learn more about Divi Areas Pro and download your copy now!
Many pre-designed layouts. Automated triggers. No coding.

Click here for more details