Coder Perfect

[duplicate] adjusting an html anchor to compensate for a fixed header

Problem

I’m attempting to improve the way my anchors function. Because I have a fixed header at the top of the page, when you link to an anchor elsewhere on the page, the page leaps to the top of the page with the anchor, leaving the content behind the fixed header (I hope that makes sense). I need a way to offset the anchor by the 25px from the height of the header. I would prefer HTML or CSS, but Javascript would be acceptable as well.

Asked by Matt Dryden

Solution #1

You could simply use CSS instead of javascript.

Give your anchor some gravitas:

<a class="anchor" id="top"></a>

By making the anchor a block element and locating it comparatively, you can place it an offset higher or lower than where it appears on the page. The anchor will be moved up 250px if you use -250px.

a.anchor {
    display: block;
    position: relative;
    top: -250px;
    visibility: hidden;
}

Answered by Jan

Solution #2

This is what I came up with:

<a name="myanchor">
    <h1 style="padding-top: 40px; margin-top: -40px;">My anchor</h1>
</a>

This leaves no gaps in the material, and anchor links perform admirably.

Answered by Alexander Savin

Solution #3

I, too, was seeking for a solution to this. In my case, it was relatively simple.

I’ve created a list menu that has all of the links:

<ul>
<li><a href="#one">one</a></li>
<li><a href="#two">two</a></li>
<li><a href="#three">three</a></li>
<li><a href="#four">four</a></li>
</ul>

Then there are the headings for where it should go.

<h3>one</h3>
<p>text here</p>

<h3>two</h3>
<p>text here</p>

<h3>three</h3>
<p>text here</p>

<h3>four</h3>
<p>text here</p>

I can’t just have it go to my tag because that would be behind the menu because I have a fixed menu at the top of my page.

Instead, I used a span tag with the appropriate id inside my tag.

<h3><span id="one"></span>one</h3>

To position them properly, use two lines of CSS.

h3{ position:relative; }
h3 span{ position:absolute; top:-200px;}

To match the height of your fixed header, change the top value (or more). Now, I’m assuming that this would also work with other elements.

Answered by Hrvoje Miljak

Solution #4

For the record, this worked for me:

[id]::before {
  content: '';
  display: block;
  height:      75px;
  margin-top: -75px;
  visibility: hidden;
}

Answered by Mark Nottingham

Solution #5

Because this is a presentation issue, a pure CSS solution would be best. However, this subject was raised in 2012, and while relative positioning / negative margin solutions have been proposed, these approaches appear hacky, introduce possible flow difficulties, and are unable to respond dynamically to changes in the DOM / viewport.

With that in mind, I believe that using JavaScript is still the best way (February 2017). A vanilla-JS solution is provided below, which will respond to anchor clicks as well as determine the page hash on load (See JSFiddle). If dynamic calculations are required, change the.getFixedOffset() method. If you’re using jQuery, here’s a tweaked version that has improved event delegation and smooth scrolling.

(function(document, history, location) {
  var HISTORY_SUPPORT = !!(history && history.pushState);

  var anchorScrolls = {
    ANCHOR_REGEX: /^#[^ ]+$/,
    OFFSET_HEIGHT_PX: 50,

    /**
     * Establish events, and fix initial scroll position if a hash is provided.
     */
    init: function() {
      this.scrollToCurrent();
      window.addEventListener('hashchange', this.scrollToCurrent.bind(this));
      document.body.addEventListener('click', this.delegateAnchors.bind(this));
    },

    /**
     * Return the offset amount to deduct from the normal scroll position.
     * Modify as appropriate to allow for dynamic calculations
     */
    getFixedOffset: function() {
      return this.OFFSET_HEIGHT_PX;
    },

    /**
     * If the provided href is an anchor which resolves to an element on the
     * page, scroll to it.
     * @param  {String} href
     * @return {Boolean} - Was the href an anchor.
     */
    scrollIfAnchor: function(href, pushToHistory) {
      var match, rect, anchorOffset;

      if(!this.ANCHOR_REGEX.test(href)) {
        return false;
      }

      match = document.getElementById(href.slice(1));

      if(match) {
        rect = match.getBoundingClientRect();
        anchorOffset = window.pageYOffset + rect.top - this.getFixedOffset();
        window.scrollTo(window.pageXOffset, anchorOffset);

        // Add the state to history as-per normal anchor links
        if(HISTORY_SUPPORT && pushToHistory) {
          history.pushState({}, document.title, location.pathname + href);
        }
      }

      return !!match;
    },

    /**
     * Attempt to scroll to the current location's hash.
     */
    scrollToCurrent: function() {
      this.scrollIfAnchor(window.location.hash);
    },

    /**
     * If the click event's target was an anchor, fix the scroll position.
     */
    delegateAnchors: function(e) {
      var elem = e.target;

      if(
        elem.nodeName === 'A' &&
        this.scrollIfAnchor(elem.getAttribute('href'), true)
      ) {
        e.preventDefault();
      }
    }
  };

  window.addEventListener(
    'DOMContentLoaded', anchorScrolls.init.bind(anchorScrolls)
  );
})(window.document, window.history, window.location);

Answered by Ian Clark

Post is based on https://stackoverflow.com/questions/10732690/offsetting-an-html-anchor-to-adjust-for-fixed-header