Liberate Hidden Headings from the Sticky Menu

Sticky menus have become a popular design feature on many websites, as they keep navigation links readily accessible while users scroll through the page. However, they often cause an annoying problem: when users click an in-page link to jump to a specific heading, that heading can get hidden beneath the sticky menu. I am thinking here of the typical horizontal menu that appears at or near the top of the page originally and that “sticks” to the top of the screen, remaining visible no matter how far down you scroll.

The Sticky Menu Problem in Detail

Let’s describe a typical scenario. Your WordPress site has a sticky menu that appears after the user has scrolled down the page some predefined distance. Now, when the user clicks an in-page link, the beginning of what should come into view next ends up hidden under the sticky menu. Often it is a section heading, which is crucial to see to get one’s bearings on the page.

While no less problematic, this issue becomes even more obvious when there is a delay in the sticky menu’s appearance. The target element is seen scrolling into view, only to be covered quickly by the sticky menu. At least if you see the heading disappear, you know you are near the right place. If the sticky menu covers it before you can see it, perhaps along with the beginning of the following paragraph, you may be left wondering where you are, completely lost, not knowing that the beginning of what you seek is just a short scroll up.

There are sticky variations where the sticky menu disappears when you are scrolling down but reappears when you start scrolling up or vice-versa. I had a problem with one website where if I clicked an in-page link that made the page scroll up, the sticky menu covered my target destination, but if I clicked a different link that made the page scroll down, the sticky menu did not appear so there was no problem.

What may seem a minor inconvenience can affect user experience significantly. Frustrated users are more likely to leave your site quickly, which is bad for business and bad for SEO.

So, how can you solve this issue? I will share one solution with you that uses jQuery, a fairly easy-to-use JavaScript library already included with WordPress.

The Solution

The code we’re going to use adjusts the scroll position based on the height of the sticky menu. It moves the linked heading just below the sticky menu, making it completely visible. It also includes a temporary overlay to lessen any possible annoyance from the page jumping around before it finally settles where it belongs. (If the sticky menu is not showing, its height is 0 and so the code does not cause the page to adjust, since no adjustment is needed.)

Here’s the code:

jQuery(document).ready(function ($) {
  // Create a new div element for the overlay
  var overlay = $(
    '<div id="overlay" style="position:fixed; top:0; left:0; height:100vh; width:100vw; background-color:rgba(100,100,100,0.7); z-index:9999;"></div>'

  // Create a new div element to hold the waiting message
  var waitMessage = $(
    '<div id="waitMessage" style="position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); text-align:center; color: white;font-size:2rem;">One moment please...</div>'

  $('a[href^="#"]').on("mousedown keydown", function (event) {

    // For mousedown event, be sure the left button was clicked
    if (event.type === "mousedown" && event.which !== 1) return;

    // For keydown event, ensure it's the Enter key being pressed
    if (event.type === "keydown" && event.key !== "Enter") return;

    var targetHref = this.getAttribute("href");
    if (targetHref === "#") return;

    var target = $(targetHref);
    if (target.length) {
      var originalTargetPosition = target.offset().top;

      // Insert the overlay

      setTimeout(function () {

        // Calculate the menu height and adjusted target position
        // (Note that we only look for the menu within the setTimeout function 
        // in case the menu has animation that makes it fade in or otherwise
        // takes time to appear.)
        var menuHeight = $("#my-sticky-menu-wrap").outerHeight(true);
        var adjustedTargetPosition = originalTargetPosition - menuHeight - 30;

        // Jump to the adjusted target position
        $('html, body').scrollTop(adjustedTargetPosition);

        // Fade out and remove the overlay
        // Adjust the fadeOut time as needed for a good visual experience
        $('#overlay').fadeOut(500, function() {

      }, 1000);  // Adjust as needed; account for time for the sticky menu to appear

Integrating the Solution into the WordPress Website

To use this code in your website, follow these steps:

  1. Create a child theme: If you haven’t already, create a child theme to ensure your modifications aren’t lost when the parent theme updates.
  2. Create a JavaScript file: In your child theme directory, create a JavaScript file. You could call it something like liberate-links.js. (To keep folders tidy, it is common practice to put jQuery and JavaScript files into a subfolder, often named “js”.)
  3. Copy the code: Paste the jQuery code given above into your newly created liberate-links.js file.
  4. Enqueue the script: Before the script will run, you need to “enqueue” it, which simply means you are telling WordPress to run it when it loads the page. Add the following code to your child theme’s functions.php file to enqueue the script:
function my_theme_enqueue_scripts() {
    wp_enqueue_script( 'custom-js', get_stylesheet_directory_uri() . '/custom.js', array( 'jquery' ), '1.0.0', true );
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_scripts' );
  1. Replace #my-sticky-menu-wrap: Replace the ‘#my-sticky-menu-wrap’ string in the code with the actual id of your sticky menu.

How to Find Out What ID to Use

You can find out the HTML id attribute for your sticky menu by using your browser’s inspection tools. Scroll down the page far enough to be sure you are looking at the sticky version of the menu. Right-click the sticky menu and click “Inspect.” This will open a page showing the HTML code that makes up your page, and you will be somewhere inside the sticky menu.

Look for an HTML tag that surrounds the sticky menu. Look for the code within the tag that says something like, id="#the-id-tag-for-the-element", where #the-id-tag-for-the-element will be an id name unique to your sticky menu, such as id="#mysticky-menu“. Some tags will not have an id attribute assigned, in which case you can either add an id in the full site editor to one of the elements already on the page and holding the sticky menu or else add a group block in the editor and drag the sticky menu inside it. If you use a plugin to create the sticky menu out of your regular menu, try assigning an id to your regular menu and using that.

My first coding attempts failed because the table of contents plugin I was using conflicted with the code I was writing. My code watched for “click” events and so, apparently, did my table of contents plugin. The plugin won. In fact, I tried several other table of contents plugins and had the same problem. If I had no such plugin active, my code successfully placed the heading I expected to jump to right below the sticky menu. I mention this because you may have a plugin I have not tested that may conflict with my code. Test everything. Adapt accordingly.

What was I to do? I chose to keep my favorite table of contents plugin and use jQuery to work around both its quirks and the sticky menu problem. Instead of an hour or two of coding and testing, it took literally all day, even with suggestions from my favorite AI personality, ChatGPT4, to speed up the process of trying different approaches. What worked in this case was using “mousedown” and “keydown” events instead of the “click” event.

The Sticky Menu in a Small Screen

When I tested at different screen sizes I found another problem.

When the screen was very small and the text was large, the sticky menu did not appear. That was not the problem but rather a good thing. This was intentional; in the sticky menu settings we had turned it off deliberately for small screens because we had our entire heading, including our logo and site title, as part of what was sticky, and there was a point where the heading covered up the entire screen, making the content inaccessible.

However, there was a somewhat larger screen size where the heading only covered up about a quarter of the screen and yet it still caused problems with scrolling correctly when in-page links were clicked. I could have changed the sticky menu settings again to turn off at this screen size too, but I chose not to. Instead, I decided to un-stick the sticky menu at this screen size by adding the following jQuery code.

    // Make the sticky menu stop being sticky at small screen sizes and then exit, 
    // as we don't need to adjust scrolling if no sticky menu is in the way.
    if($(window).width() < 450) {  

The code above makes the menu stay at the top of the page rather than reappearing as you scroll. You must go back to the very top of the page to find it again. You may find you need to do something similar at some of the smaller screen sizes.

I did not want to remove the sticky menu when scrolling normally. It was only a problem if you clicked an in-page link, so only at that point did I want it to stop sticking. Therefore, I put it inside the code that handles the mousedown and keydown events, right after these lines:

$('a[href^="#"]').on("mousedown keydown", function (event) {
    // For keydown event, ensure it's the Enter key being pressed
    if (event.type === "keydown" && event.key !== "Enter") return; [... put it right after this line]

Your needs may be somewhat different, so you may need to alter the code accordingly.

In Conclusion: A Better Sticky Menu User Experience

Now when your users click what look like helpful in-page links, they will go where they expect to go rather than landing in the middle of something, wondering what to do next and what went wrong. You have liberated not only your hidden heading anchors from your sticky menu, but also your users–or at the very least, you saved their sanity!

I hope you enjoy making things right as much as I do. The Internet has too much built-in user frustration already. When everything works smoothly without functional or visual annoyances, your users may not appreciate how much effort went into making the site work, but you can be sure they will notice when it doesn’t.

I wish you all the rewards of a well-designed site, one that keeps users happy and brings you whatever connections and business you may seek!

If you would like help with your WordPress site, just contact me and I will be happy to discuss your needs.

Leave a Comment