0

I could not figure out how to offset an HTML anchor to adjust for a fixed header, but this solution worked in the beginning: https://stackoverflow.com/a/13067009/11309106

(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);

A new problem that arises is that now when I click the anchor link from another page, it does not offset properly.

For example, if you click on "About" from the following page https://lisaschumann.co.uk/blog/blog.html what ends up happening is:

  1. it does not offset properly +
  2. in case of the /#about link it distorts the image at the beginning of the about section. (It displays fine once you resize the window from there).

For 1. I found this suggestion in the comments: "I had links to other pages with anchors as well, e.g. /page/#anchor. This regex covers those: /^[/a-z0-9-]#[^\s]+$/"* but it still does not work.

Any help is greatly appreciated.

Lisa Schumann
  • 117
  • 10
  • Your about link does not work from the blog page. Change the href from About to About :) – Tiberiuscan Oct 14 '19 at 11:07
  • Thanks for your answer @Tiberiuscan. But I would like it to link to the "About" section on the index.html page. – Lisa Schumann Oct 14 '19 at 19:55
  • Then you would need to change it to `About` You would need to add a name attribute to your about section div like `
    – Tiberiuscan Oct 15 '19 at 18:35
  • Hi @Tiberiuscan - I'm so confused. It is already linking to the right section but it is not offsetting properly if I click it from another page! The Javascript offset only works properly when the link is clicked from the same page. – Lisa Schumann Oct 16 '19 at 12:05
  • So what I am doing is clicking on the link you provided above (https://lisaschumann.co.uk/blog/blog.html) then clicking on About in the navigation but this results in a 404. That's why I said this needs fixing so I can see the main bug at hand. The about link only works for me if I am on the home page. – Tiberiuscan Oct 16 '19 at 14:57
  • Gotcha! I'm really sorry there was a hang-up with the content negotiation setting. I changed the links back now - if you wouldn't mind taking another look, @Tiberiuscan. Really appreciate your help. – Lisa Schumann Oct 21 '19 at 08:26
  • I'll take a look a bit later :) – Tiberiuscan Oct 22 '19 at 09:31

1 Answers1

0

I found an answer. This fixed the issue - setting a timeout to the execution:

// Navigation Anchor with correct margin to display sections
(function (document, history, location) {
  var HISTORY_SUPPORT = !!(history && history.pushState);
  var anchorScrolls = {
    //ANCHOR_REGEX: /^#[^ ]+$/
    ANCHOR_REGEX: /^[\/a-z0-9-.]*#[^\s]+$/,
    OFFSET_HEIGHT_PX: 58,

    /**
     * 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();

        if (pushToHistory) {
          window.scrollTo(window.pageXOffset, anchorOffset);
        } else {
          // Allow page content to load
          setTimeout(function () {
            window.scrollTo(window.pageXOffset, anchorOffset);
          }, 40);
        }

        // 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);
Lisa Schumann
  • 117
  • 10