I am building an action bar for one of my projects nad needed it to be fixed whenever the module reaches the top scroll position. I decided to build a quick prototype in codepen.

Goals:
1. It needs to be library agnostic, no JS library dependencies.
2. Simple API -- Simple way to implement the module.
3. CSS Should be simple and easy to extend.
4. Write it in ES6, so that I can learn ES6.

Scroll Top?

In order to determine the top scroll position you could use the window.scrollY property, if it were available. The reality is, that I'll need this solution to work in various browsers. So I will write a tiny helper module that will expose a tiny api, exposing the current X and Y scroll positions.

Note: All JS examples will be written in ES6 syntax using the Traceur compiler.

First things first, I create a global doc and win object to help me out a little. I take advantage of the ES6 destructuring assignment.

const [doc, win] = [document, window]; // var doc = document, win = window;  
winOffset
var winOffset = (()=>{

  const isCSS1Compat  = ((doc.compatMode || '') === 'CSS1Compat');
  const supportPageOffset = win.pageXOffset !== undefined;

  var fn =  { x: () => win.pageXOffset, y: () => win.pageYOffset };

  if(supportPageOffset) { return fn };

  var docCompat = isCSS1Compat ? doc.documentElement : doc.body;

  fn.x = () => docCompat.scrollLeft;
  fn.y = () => docCompat.scrollTop;

  return fn;

})();

What happens here is simple. The winOffse module will be assigned an object literal with two methods: winOffset.x() and winOffset.y().

The IIFE determines if the pageXOffset (an alias for scrollX) property is supported.

  const isCSS1Compat  = ((doc.compatMode || '') === 'CSS1Compat');
  const supportPageOffset = win.pageXOffset !== undefined;

  var fn =  { x: () => win.pageXOffset, y: () => win.pageYOffset };

  if(supportPageOffset) { return fn };

If it is not supported, then we need to work around it:

  // Determine what document property to use depending on the CSS1 compatibility string
  var docCompat = isCSS1Compat ? doc.documentElement : doc.body;

  // Re-assign the x and y methods to return the scrolLLeft and scrollTop properties from the document object
  fn.x = () => docCompat.scrollLeft;
  fn.y = () => docCompat.scrollTop;

We can now determine what the scroll offset is by calling either winOffset.x() or winOffset.y().

Fixed Navigation Module

The fixed navigation module is an ES6 class. The constructor will expect a dom object which will inherit the fixed navigation behavior.

class FixedNav {

  get cls() {
    return {
      nav: 'fixed',
      body: 'nav-body-fixed'
     };
  }

  constructor(el){
    this.el = el;
    this.body = doc.body;
      this.navOffset = this.el.offsetTop;
    this.bind();
  }

  bind() {
    win.addEventListener('scroll', this.build.bind(this));
  }

  build() {
    var [nav, body] = [this.cls.nav, this.cls.body];
    if(winOffset.y() > this.navOffset) {
      this.el.classList.add(nav);
      this.body.classList.add(body);
    } else {
      this.el.classList.remove(nav);
      this.body.classList.remove(body);
    }
  }
}

The constructor determines the elements top offset, then binds to the window scroll event. Whenever the window scroll event is fired, the fixed nav modules build() method is called, and this is where the appropriate fixed classes are applied depending on whether the scroll offset exceeds the nav elements top offset.

  build() {
    var [nav, body] = [this.cls.nav, this.cls.body]; / var nav = this.cls.nav, body = this.cls.body;

    // If scroll offset exceeds the elements offset.
    if(winOffset.y() > this.navOffset) {
      this.el.classList.add(nav);
      this.body.classList.add(body);
    } else {
      this.el.classList.remove(nav);
      this.body.classList.remove(body);
    }

  }
Using the module

Now all we have to do is create the module.

<nav>Navigation</nav>  
new FixedNav(doc.querySelector('nav'));  
CodePen Example:

See the Pen Fixed Nav by Antonio Lettieri (@alettieri) on CodePen.