Scrolling to an anchor given fixed position navbar

by ianclark001

HTML

<div id="fixed">
  My fixed bar
</div>

<div id="targets">
  <div id="target-1">
    <a href="#target-2">Jump to target 2</a>
  </div>
  <div id="target-2">
    <a href="#target-3">Jump to target 3</a>
  </div>
  <div id="target-3">
    <a href="#target-4">Jump to target 4</a>
  </div>
  <div id="target-4">
    <a href="#target-5">Jump to target 5</a>
  </div>
  <div id="target-5">
    <a href="#target-1">Jump to target 1</a>
  </div>
</div>

CSS

body {
  padding: 0;
  margin: 50px 0 0;
  font-family: "Arial";
  font-size: 1em;
}

a {
  color: #333;
}

#fixed {
  position: fixed;
  height: 50px;
  line-height: 50px;
  background: #000;
  top: 0;
  left: 0;
  right: 0;
  color: #fff;
  padding-left: 5px;
}

#targets div {
  height: 100vh;
  padding: 10px;
  color: #333;
}

#target-1 {
  background: #888;
}

#target-2 {
  background: #999;
}

#target-3 {
  background: #AAA;
}

#target-4 {
  background: #BBB;
}

#target-5 {
  background: #CCC;
}

JavaScript

(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'),...