Advertisement
Guest User

headroom

a guest
May 16th, 2018
180
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <!-- header behavior -->
  2. <script>
  3. /*!
  4.  * headroom.js v0.9.4 - Give your page some headroom. Hide your header until you need it
  5.  * Copyright (c) 2017 Nick Williams - http://wicky.nillia.ms/headroom.js
  6.  * License: MIT
  7.  */
  8.  
  9. (function(root, factory) {
  10.   'use strict';
  11.  
  12.   if (typeof define === 'function' && define.amd) {
  13.     // AMD. Register as an anonymous module.
  14.     define([], factory);
  15.   }
  16.   else if (typeof exports === 'object') {
  17.     // COMMONJS
  18.     module.exports = factory();
  19.   }
  20.   else {
  21.     // BROWSER
  22.     root.Headroom = factory();
  23.   }
  24. }(this, function() {
  25.   'use strict';
  26.  
  27.   /* exported features */
  28.  
  29.   var features = {
  30.     bind : !!(function(){}.bind),
  31.     classList : 'classList' in document.documentElement,
  32.     rAF : !!(window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame)
  33.   };
  34.   window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
  35.  
  36.   /**
  37.    * Handles debouncing of events via requestAnimationFrame
  38.    * @see http://www.html5rocks.com/en/tutorials/speed/animations/
  39.    * @param {Function} callback The callback to handle whichever event
  40.    */
  41.   function Debouncer (callback) {
  42.     this.callback = callback;
  43.     this.ticking = false;
  44.   }
  45.   Debouncer.prototype = {
  46.     constructor : Debouncer,
  47.  
  48.     /**
  49.      * dispatches the event to the supplied callback
  50.      * @private
  51.      */
  52.     update : function() {
  53.       this.callback && this.callback();
  54.       this.ticking = false;
  55.     },
  56.  
  57.     /**
  58.      * ensures events don't get stacked
  59.      * @private
  60.      */
  61.     requestTick : function() {
  62.       if(!this.ticking) {
  63.         requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this)));
  64.         this.ticking = true;
  65.       }
  66.     },
  67.  
  68.     /**
  69.      * Attach this as the event listeners
  70.      */
  71.     handleEvent : function() {
  72.       this.requestTick();
  73.     }
  74.   };
  75.   /**
  76.    * Check if object is part of the DOM
  77.    * @constructor
  78.    * @param {Object} obj element to check
  79.    */
  80.   function isDOMElement(obj) {
  81.     return obj && typeof window !== 'undefined' && (obj === window || obj.nodeType);
  82.   }
  83.  
  84.   /**
  85.    * Helper function for extending objects
  86.    */
  87.   function extend (object /*, objectN ... */) {
  88.     if(arguments.length <= 0) {
  89.       throw new Error('Missing arguments in extend function');
  90.     }
  91.  
  92.     var result = object || {},
  93.         key,
  94.         i;
  95.  
  96.     for (i = 1; i < arguments.length; i++) {
  97.       var replacement = arguments[i] || {};
  98.  
  99.       for (key in replacement) {
  100.         // Recurse into object except if the object is a DOM element
  101.         if(typeof result[key] === 'object' && ! isDOMElement(result[key])) {
  102.           result[key] = extend(result[key], replacement[key]);
  103.         }
  104.         else {
  105.           result[key] = result[key] || replacement[key];
  106.         }
  107.       }
  108.     }
  109.  
  110.     return result;
  111.   }
  112.  
  113.   /**
  114.    * Helper function for normalizing tolerance option to object format
  115.    */
  116.   function normalizeTolerance (t) {
  117.     return t === Object(t) ? t : { down : t, up : t };
  118.   }
  119.  
  120.   /**
  121.    * UI enhancement for fixed headers.
  122.    * Hides header when scrolling down
  123.    * Shows header when scrolling up
  124.    * @constructor
  125.    * @param {DOMElement} elem the header element
  126.    * @param {Object} options options for the widget
  127.    */
  128.   function Headroom (elem, options) {
  129.     options = extend(options, Headroom.options);
  130.  
  131.     this.lastKnownScrollY = 0;
  132.     this.elem             = elem;
  133.     this.tolerance        = normalizeTolerance(options.tolerance);
  134.     this.classes          = options.classes;
  135.     this.offset           = options.offset;
  136.     this.scroller         = options.scroller;
  137.     this.initialised      = false;
  138.     this.onPin            = options.onPin;
  139.     this.onUnpin          = options.onUnpin;
  140.     this.onTop            = options.onTop;
  141.     this.onNotTop         = options.onNotTop;
  142.     this.onBottom         = options.onBottom;
  143.     this.onNotBottom      = options.onNotBottom;
  144.   }
  145.   Headroom.prototype = {
  146.     constructor : Headroom,
  147.  
  148.     /**
  149.      * Initialises the widget
  150.      */
  151.     init : function() {
  152.       if(!Headroom.cutsTheMustard) {
  153.         return;
  154.       }
  155.  
  156.       this.debouncer = new Debouncer(this.update.bind(this));
  157.       this.elem.classList.add(this.classes.initial);
  158.  
  159.       // defer event registration to handle browser
  160.       // potentially restoring previous scroll position
  161.       setTimeout(this.attachEvent.bind(this), 100);
  162.  
  163.       return this;
  164.     },
  165.  
  166.     /**
  167.      * Unattaches events and removes any classes that were added
  168.      */
  169.     destroy : function() {
  170.       var classes = this.classes;
  171.  
  172.       this.initialised = false;
  173.  
  174.       for (var key in classes) {
  175.         if(classes.hasOwnProperty(key)) {
  176.           this.elem.classList.remove(classes[key]);
  177.         }
  178.       }
  179.  
  180.       this.scroller.removeEventListener('scroll', this.debouncer, false);
  181.     },
  182.  
  183.     /**
  184.      * Attaches the scroll event
  185.      * @private
  186.      */
  187.     attachEvent : function() {
  188.       if(!this.initialised){
  189.         this.lastKnownScrollY = this.getScrollY();
  190.         this.initialised = true;
  191.         this.scroller.addEventListener('scroll', this.debouncer, false);
  192.  
  193.         this.debouncer.handleEvent();
  194.       }
  195.     },
  196.  
  197.     /**
  198.      * Unpins the header if it's currently pinned
  199.      */
  200.     unpin : function() {
  201.       var classList = this.elem.classList,
  202.         classes = this.classes;
  203.  
  204.       if(classList.contains(classes.pinned) || !classList.contains(classes.unpinned)) {
  205.         classList.add(classes.unpinned);
  206.         classList.remove(classes.pinned);
  207.         this.onUnpin && this.onUnpin.call(this);
  208.       }
  209.     },
  210.  
  211.     /**
  212.      * Pins the header if it's currently unpinned
  213.      */
  214.     pin : function() {
  215.       var classList = this.elem.classList,
  216.         classes = this.classes;
  217.  
  218.       if(classList.contains(classes.unpinned)) {
  219.         classList.remove(classes.unpinned);
  220.         classList.add(classes.pinned);
  221.         this.onPin && this.onPin.call(this);
  222.       }
  223.     },
  224.  
  225.     /**
  226.      * Handles the top states
  227.      */
  228.     top : function() {
  229.       var classList = this.elem.classList,
  230.         classes = this.classes;
  231.  
  232.       if(!classList.contains(classes.top)) {
  233.         classList.add(classes.top);
  234.         classList.remove(classes.notTop);
  235.         this.onTop && this.onTop.call(this);
  236.       }
  237.     },
  238.  
  239.     /**
  240.      * Handles the not top state
  241.      */
  242.     notTop : function() {
  243.       var classList = this.elem.classList,
  244.         classes = this.classes;
  245.  
  246.       if(!classList.contains(classes.notTop)) {
  247.         classList.add(classes.notTop);
  248.         classList.remove(classes.top);
  249.         this.onNotTop && this.onNotTop.call(this);
  250.       }
  251.     },
  252.  
  253.     bottom : function() {
  254.       var classList = this.elem.classList,
  255.         classes = this.classes;
  256.  
  257.       if(!classList.contains(classes.bottom)) {
  258.         classList.add(classes.bottom);
  259.         classList.remove(classes.notBottom);
  260.         this.onBottom && this.onBottom.call(this);
  261.       }
  262.     },
  263.  
  264.     /**
  265.      * Handles the not top state
  266.      */
  267.     notBottom : function() {
  268.       var classList = this.elem.classList,
  269.         classes = this.classes;
  270.  
  271.       if(!classList.contains(classes.notBottom)) {
  272.         classList.add(classes.notBottom);
  273.         classList.remove(classes.bottom);
  274.         this.onNotBottom && this.onNotBottom.call(this);
  275.       }
  276.     },
  277.  
  278.     /**
  279.      * Gets the Y scroll position
  280.      * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.scrollY
  281.      * @return {Number} pixels the page has scrolled along the Y-axis
  282.      */
  283.     getScrollY : function() {
  284.       return (this.scroller.pageYOffset !== undefined)
  285.         ? this.scroller.pageYOffset
  286.         : (this.scroller.scrollTop !== undefined)
  287.           ? this.scroller.scrollTop
  288.           : (document.documentElement || document.body.parentNode || document.body).scrollTop;
  289.     },
  290.  
  291.     /**
  292.      * Gets the height of the viewport
  293.      * @see http://andylangton.co.uk/blog/development/get-viewport-size-width-and-height-javascript
  294.      * @return {int} the height of the viewport in pixels
  295.      */
  296.     getViewportHeight : function () {
  297.       return window.innerHeight
  298.         || document.documentElement.clientHeight
  299.         || document.body.clientHeight;
  300.     },
  301.  
  302.     /**
  303.      * Gets the physical height of the DOM element
  304.      * @param  {Object}  elm the element to calculate the physical height of which
  305.      * @return {int}     the physical height of the element in pixels
  306.      */
  307.     getElementPhysicalHeight : function (elm) {
  308.       return Math.max(
  309.         elm.offsetHeight,
  310.         elm.clientHeight
  311.       );
  312.     },
  313.  
  314.     /**
  315.      * Gets the physical height of the scroller element
  316.      * @return {int} the physical height of the scroller element in pixels
  317.      */
  318.     getScrollerPhysicalHeight : function () {
  319.       return (this.scroller === window || this.scroller === document.body)
  320.         ? this.getViewportHeight()
  321.         : this.getElementPhysicalHeight(this.scroller);
  322.     },
  323.  
  324.     /**
  325.      * Gets the height of the document
  326.      * @see http://james.padolsey.com/javascript/get-document-height-cross-browser/
  327.      * @return {int} the height of the document in pixels
  328.      */
  329.     getDocumentHeight : function () {
  330.       var body = document.body,
  331.         documentElement = document.documentElement;
  332.  
  333.       return Math.max(
  334.         body.scrollHeight, documentElement.scrollHeight,
  335.         body.offsetHeight, documentElement.offsetHeight,
  336.         body.clientHeight, documentElement.clientHeight
  337.       );
  338.     },
  339.  
  340.     /**
  341.      * Gets the height of the DOM element
  342.      * @param  {Object}  elm the element to calculate the height of which
  343.      * @return {int}     the height of the element in pixels
  344.      */
  345.     getElementHeight : function (elm) {
  346.       return Math.max(
  347.         elm.scrollHeight,
  348.         elm.offsetHeight,
  349.         elm.clientHeight
  350.       );
  351.     },
  352.  
  353.     /**
  354.      * Gets the height of the scroller element
  355.      * @return {int} the height of the scroller element in pixels
  356.      */
  357.     getScrollerHeight : function () {
  358.       return (this.scroller === window || this.scroller === document.body)
  359.         ? this.getDocumentHeight()
  360.         : this.getElementHeight(this.scroller);
  361.     },
  362.  
  363.     /**
  364.      * determines if the scroll position is outside of document boundaries
  365.      * @param  {int}  currentScrollY the current y scroll position
  366.      * @return {bool} true if out of bounds, false otherwise
  367.      */
  368.     isOutOfBounds : function (currentScrollY) {
  369.       var pastTop  = currentScrollY < 0,
  370.         pastBottom = currentScrollY + this.getScrollerPhysicalHeight() > this.getScrollerHeight();
  371.  
  372.       return pastTop || pastBottom;
  373.     },
  374.  
  375.     /**
  376.      * determines if the tolerance has been exceeded
  377.      * @param  {int} currentScrollY the current scroll y position
  378.      * @return {bool} true if tolerance exceeded, false otherwise
  379.      */
  380.     toleranceExceeded : function (currentScrollY, direction) {
  381.       return Math.abs(currentScrollY-this.lastKnownScrollY) >= this.tolerance[direction];
  382.     },
  383.  
  384.     /**
  385.      * determine if it is appropriate to unpin
  386.      * @param  {int} currentScrollY the current y scroll position
  387.      * @param  {bool} toleranceExceeded has the tolerance been exceeded?
  388.      * @return {bool} true if should unpin, false otherwise
  389.      */
  390.     shouldUnpin : function (currentScrollY, toleranceExceeded) {
  391.       var scrollingDown = currentScrollY > this.lastKnownScrollY,
  392.         pastOffset = currentScrollY >= this.offset;
  393.  
  394.       return scrollingDown && pastOffset && toleranceExceeded;
  395.     },
  396.  
  397.     /**
  398.      * determine if it is appropriate to pin
  399.      * @param  {int} currentScrollY the current y scroll position
  400.      * @param  {bool} toleranceExceeded has the tolerance been exceeded?
  401.      * @return {bool} true if should pin, false otherwise
  402.      */
  403.     shouldPin : function (currentScrollY, toleranceExceeded) {
  404.       var scrollingUp  = currentScrollY < this.lastKnownScrollY,
  405.         pastOffset = currentScrollY <= this.offset;
  406.  
  407.       return (scrollingUp && toleranceExceeded) || pastOffset;
  408.     },
  409.  
  410.     /**
  411.      * Handles updating the state of the widget
  412.      */
  413.     update : function() {
  414.       var currentScrollY  = this.getScrollY(),
  415.         scrollDirection = currentScrollY > this.lastKnownScrollY ? 'down' : 'up',
  416.         toleranceExceeded = this.toleranceExceeded(currentScrollY, scrollDirection);
  417.  
  418.       if(this.isOutOfBounds(currentScrollY)) { // Ignore bouncy scrolling in OSX
  419.         return;
  420.       }
  421.  
  422.       if (currentScrollY <= this.offset ) {
  423.         this.top();
  424.       } else {
  425.         this.notTop();
  426.       }
  427.  
  428.       if(currentScrollY + this.getViewportHeight() >= this.getScrollerHeight()) {
  429.         this.bottom();
  430.       }
  431.       else {
  432.         this.notBottom();
  433.       }
  434.  
  435.       if(this.shouldUnpin(currentScrollY, toleranceExceeded)) {
  436.         this.unpin();
  437.       }
  438.       else if(this.shouldPin(currentScrollY, toleranceExceeded)) {
  439.         this.pin();
  440.       }
  441.  
  442.       this.lastKnownScrollY = currentScrollY;
  443.     }
  444.   };
  445.   /**
  446.    * Default options
  447.    * @type {Object}
  448.    */
  449.   Headroom.options = {
  450.     tolerance : {
  451.       up : 0,
  452.       down : 0
  453.     },
  454.     offset : 0,
  455.     scroller: window,
  456.     classes : {
  457.       pinned : 'headroom--pinned',
  458.       unpinned : 'headroom--unpinned',
  459.       top : 'headroom--top',
  460.       notTop : 'headroom--not-top',
  461.       bottom : 'headroom--bottom',
  462.       notBottom : 'headroom--not-bottom',
  463.       initial : 'headroom'
  464.     }
  465.   };
  466.   Headroom.cutsTheMustard = typeof features !== 'undefined' && features.rAF && features.bind && features.classList;
  467.  
  468.   return Headroom;
  469. }));
  470.  
  471. // grab an element
  472. var myElement = document.querySelector("#header");
  473. // construct an instance of Headroom, passing the element
  474. var headroom  = new Headroom(myElement);
  475. // initialise
  476. headroom.init();
  477.  
  478. var headroom = new Headroom(elem, {
  479.   "offset": 205,
  480.   "tolerance": 5,
  481.   "classes": {
  482.     "initial": "animated",
  483.     "pinned": "slideDown",
  484.     "unpinned": "slideUp"
  485.   }
  486. });
  487. headroom.init();
  488.  
  489. // to destroy
  490. headroom.destroy();
  491. </script>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement