affixing-header
Advanced tools
Comparing version 0.2.1 to 0.3.1
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
global.affixingHeader = factory() | ||
}(this, function () { 'use strict'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('onscrolling')) : | ||
typeof define === 'function' && define.amd ? define(['onscrolling'], factory) : | ||
global.affixingHeader = factory(global.onscrolling) | ||
}(this, function (onscrolling) { 'use strict'; | ||
'use strict'; | ||
'use strict'; | ||
var requestFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame, | ||
isSupported = requestFrame !== undefined, | ||
isListening = false, | ||
isQueued = false, | ||
onscroll__scrollY = window.pageYOffset, | ||
scrollX = window.pageXOffset, | ||
scrollYCached = onscroll__scrollY, | ||
scrollXCached = scrollX, | ||
directionX = ['x', 'horizontal'], | ||
// directionY = [ 'y', 'vertical'], | ||
directionAll = ['any'], | ||
callbackQueue = { | ||
x : [], | ||
y : [], | ||
any : [] | ||
}, | ||
onscroll__handleScroll, | ||
onscroll__onScrollDebouncer, | ||
onscroll__onscrolling; | ||
var scrollYPrev = 0, | ||
scrollY = 0, | ||
upScrollCount = 0, | ||
//downScrollCount = 0, | ||
isNavAffixed = false, | ||
isNavTransitioning = false, | ||
header; | ||
function onscroll__handleScroll() { | ||
var i; | ||
function affixNavBar() { | ||
isNavAffixed = true; | ||
isNavTransitioning = false; | ||
header.style.top = 0; | ||
header.style.position = 'fixed'; | ||
} | ||
if (onscroll__scrollY !== scrollYCached) { | ||
for (i = 0; i < callbackQueue.y.length; i++) { | ||
callbackQueue.y[i](onscroll__scrollY); | ||
} | ||
scrollYCached = onscroll__scrollY; | ||
} | ||
if (scrollX !== scrollXCached) { | ||
for (i = 0; i < callbackQueue.x.length; i++) { | ||
callbackQueue.x[i](scrollX); | ||
} | ||
scrollXCached = scrollX; | ||
} | ||
for (i = 0; i < callbackQueue.any.length; i++) { | ||
callbackQueue.any[i]([scrollX, onscroll__scrollY]); | ||
} | ||
function unAffixNavBar() { | ||
if (!isNavAffixed) { | ||
// Nothing to do here | ||
return; | ||
} | ||
upScrollCount = 0; | ||
isNavAffixed = false; | ||
// Only set top position for switch from fixed absolute if not transitioning | ||
if (!isNavTransitioning) { | ||
// If user jumped down the page (e.g. paging with spacebar) | ||
if (scrollY > scrollYPrev + header.clientHeight + 5) { | ||
header.style.top = scrollYPrev + 5 + 'px'; | ||
} else { | ||
header.style.top = scrollY + 'px'; | ||
} | ||
} else { | ||
isNavTransitioning = false; | ||
} | ||
header.style.position = 'absolute'; | ||
} | ||
isQueued = false; | ||
} | ||
function checkNavPosition() { | ||
if (!isNavAffixed && header.offsetTop > scrollY) { | ||
affixNavBar(); | ||
} | ||
} | ||
function requestTick() { | ||
if (!isQueued) { | ||
requestFrame(onscroll__handleScroll); | ||
} | ||
isQueued = true; | ||
} | ||
function handleScroll(scrollYCurrent) { | ||
scrollY = scrollYCurrent; | ||
// Make sure that the nav bar doesn't wind up stranded in the middle of the page | ||
checkNavPosition(); | ||
// If this is bounce scrolling (e.g. Mac OS, iOS), bail | ||
// Another way to check the top | ||
//(scrollY + window.innerHeight) > document.documentElement.scrollHeight | ||
if (scrollY < 0 || document.documentElement.scrollHeight - document.documentElement.scrollTop < document.documentElement.clientHeight) { | ||
return; | ||
} | ||
if (scrollY < scrollYPrev) { | ||
// If the user has scrolled up quickly / jumped up (like shift-spacebar) | ||
// Or we are transitioning and have reached the top of the bar | ||
if ((!isNavAffixed && scrollY + header.clientHeight + 10 < scrollYPrev) || (isNavTransitioning && scrollY <= header.offsetTop + 2)) { | ||
affixNavBar(); | ||
} else if (!isNavAffixed) { | ||
if (upScrollCount > 6) { | ||
//downScrollCount = 0; | ||
isNavAffixed = true; | ||
// If the navbar is not currently visible, set the top to just above the viewport so it appears as we scroll up | ||
if (scrollY > header.offsetTop + header.clientHeight + 5) { | ||
header.style.top = (scrollY - header.clientHeight) + 'px'; | ||
} | ||
isNavTransitioning = true; | ||
} | ||
upScrollCount++; | ||
} | ||
} else if (isNavAffixed) { | ||
unAffixNavBar(); | ||
} | ||
scrollYPrev = scrollY; | ||
} | ||
function onscroll__onScrollDebouncer() { | ||
if (callbackQueue.x.length || callbackQueue.any.length) { | ||
scrollX = window.pageXOffset; | ||
} | ||
if (callbackQueue.y.length || callbackQueue.any.length) { | ||
onscroll__scrollY = window.pageYOffset; | ||
} | ||
requestTick(); | ||
} | ||
return function(navElement) { | ||
if (!navElement) { | ||
return; | ||
} | ||
// Set initial state | ||
header = navElement; | ||
header.style.top = 0; | ||
header.style.position = 'absolute'; | ||
// Use onscrolling helper to listen for scroll changes | ||
onscrolling(handleScroll); | ||
}; | ||
/** | ||
* Attach callback to debounced scroll event | ||
* | ||
* Takes two forms: | ||
* @param function callback Function to attach to a vertical scroll event | ||
* Or: | ||
* @param string direction Direction of scroll to attach to: | ||
* 'horizontal'/'x', 'vertical'/'y' (the default), | ||
* or 'any' (listens to both) | ||
* @param function callback Function to attach to a scroll event in specified direction | ||
*/ | ||
function onscroll__onscrolling(direction, callback) { | ||
if (!isSupported) { | ||
return; | ||
} | ||
if (!isListening) { | ||
window.addEventListener('scroll', onscroll__onScrollDebouncer); | ||
document.body.addEventListener('touchmove', onscroll__onScrollDebouncer); | ||
isListening = true; | ||
} | ||
// Verify parameters | ||
if (typeof direction === 'function') { | ||
callback = direction; | ||
callbackQueue.y.push(callback); | ||
return; | ||
} | ||
if (typeof callback === 'function') { | ||
if (~directionX.indexOf(direction)) { | ||
callbackQueue.x.push(callback); | ||
} else if (~directionAll.indexOf(direction)) { | ||
callbackQueue.any.push(callback); | ||
} else { | ||
callbackQueue.y.push(callback); | ||
} | ||
} | ||
} | ||
onscroll__onscrolling.remove = function(direction, fn) { | ||
var queueKey = 'y', | ||
queue, | ||
fnIdx; | ||
if (typeof direction === 'string') { | ||
// If second parameter is not a function, return | ||
if (typeof fn !== 'function') { | ||
return; | ||
} | ||
if (~directionX.indexOf(direction)) { | ||
queueKey = directionX[0]; | ||
} else if (~directionAll.indexOf(direction)) { | ||
queueKey = directionAll[0]; | ||
} | ||
} else { | ||
fn = direction; | ||
} | ||
queue = callbackQueue[queueKey]; | ||
fnIdx = queue.indexOf(fn); | ||
if (fnIdx > -1) { | ||
queue.splice(fnIdx, 1); | ||
} | ||
}; | ||
onscroll__onscrolling.off = onscroll__onscrolling.remove; | ||
var onscroll = onscroll__onscrolling; | ||
'use strict'; | ||
var scrollYPrev = 0, | ||
affixing_header__scrollY = 0, | ||
upScrollCount = 0, | ||
//downScrollCount = 0, | ||
isNavAffixed = false, | ||
isNavTransitioning = false, | ||
header; | ||
function affixNavBar() { | ||
isNavAffixed = true; | ||
isNavTransitioning = false; | ||
header.style.top = 0; | ||
header.style.position = 'fixed'; | ||
} | ||
function unAffixNavBar() { | ||
if (!isNavAffixed) { | ||
// Nothing to do here | ||
return; | ||
} | ||
upScrollCount = 0; | ||
isNavAffixed = false; | ||
// Only set top position for switch from fixed absolute if not transitioning | ||
if (!isNavTransitioning) { | ||
// If user jumped down the page (e.g. paging with spacebar) | ||
if (affixing_header__scrollY > scrollYPrev + header.clientHeight + 5) { | ||
header.style.top = scrollYPrev + 5 + 'px'; | ||
} else { | ||
header.style.top = affixing_header__scrollY + 'px'; | ||
} | ||
} else { | ||
isNavTransitioning = false; | ||
} | ||
header.style.position = 'absolute'; | ||
} | ||
function checkNavPosition() { | ||
if (!isNavAffixed && header.offsetTop > affixing_header__scrollY) { | ||
affixNavBar(); | ||
} | ||
} | ||
function affixing_header__handleScroll(scrollYCurrent) { | ||
affixing_header__scrollY = scrollYCurrent; | ||
// Make sure that the nav bar doesn't wind up stranded in the middle of the page | ||
checkNavPosition(); | ||
// If this is bounce scrolling (e.g. Mac OS, iOS), bail | ||
// Another way to check the top | ||
//(scrollY + window.innerHeight) > document.documentElement.scrollHeight | ||
if (affixing_header__scrollY < 0 || document.documentElement.scrollHeight - document.documentElement.scrollTop < document.documentElement.clientHeight) { | ||
return; | ||
} | ||
if (affixing_header__scrollY < scrollYPrev) { | ||
// If the user has scrolled up quickly / jumped up (like shift-spacebar) | ||
// Or we are transitioning and have reached the top of the bar | ||
if ((!isNavAffixed && affixing_header__scrollY + header.clientHeight + 10 < scrollYPrev) || (isNavTransitioning && affixing_header__scrollY <= header.offsetTop + 2)) { | ||
affixNavBar(); | ||
} else if (!isNavAffixed) { | ||
if (upScrollCount > 6) { | ||
//downScrollCount = 0; | ||
isNavAffixed = true; | ||
// If the navbar is not currently visible, set the top to just above the viewport so it appears as we scroll up | ||
if (affixing_header__scrollY > header.offsetTop + header.clientHeight + 5) { | ||
header.style.top = (affixing_header__scrollY - header.clientHeight) + 'px'; | ||
} | ||
isNavTransitioning = true; | ||
} | ||
upScrollCount++; | ||
} | ||
} else if (isNavAffixed) { | ||
unAffixNavBar(); | ||
} | ||
scrollYPrev = affixing_header__scrollY; | ||
} | ||
var affixing_header = function(navElement) { | ||
if (!navElement) { | ||
return; | ||
} | ||
// Set initial state | ||
header = navElement; | ||
header.style.top = 0; | ||
header.style.position = 'absolute'; | ||
// Use onscroll helper to listen for scroll changes | ||
onscroll(affixing_header__handleScroll); | ||
} | ||
return affixing_header; | ||
})); |
{ | ||
"name": "affixing-header", | ||
"version": "0.2.1", | ||
"version": "0.3.1", | ||
"description": "An affixing header that behaves normally as a user navigates down a page, but reveals itself naturally when a user scrolls or drags upwards. Inspired by iOS Safari, Medium, and others.", | ||
"main": "dist/affixing-header.js", | ||
"scripts": { | ||
"test": "node test/bootstrap.js", | ||
"test-mocha": "mocha --reporter spec test/bootstrap.js", | ||
"build": "esperanto -b -i src/affixing-header.js -o dist/affixing-header.js -t umd -n affixingHeader", | ||
"build-install": "npm install -g esperanto;" | ||
"test": "npm run build && npm run lint:tests && node test/bootstrap.js", | ||
"test:mocha": "mocha --reporter spec test/bootstrap.js", | ||
"lint:src": "jshint src/affixing-header.js", | ||
"lint:tests": "jshint test/*.js && jshint --extract=auto test/*.html", | ||
"build": "npm run lint:src && npm run build:normal && npm run build:bundle && npm run build:minify", | ||
"build:normal": "esperanto --type umd --name affixingHeader --basedir=node_modules/onscrolling/src/ -i src/affixing-header.js -o dist/affixing-header.js", | ||
"build:bundle": "esperanto --bundle --type umd --name affixingHeader --basedir=node_modules/onscrolling/src/ -i src/affixing-header.js -o dist/affixing-header-bundled.js", | ||
"build:minify": "uglifyjs dist/affixing-header-bundled.js --screw-ie8 --mangle -o dist/affixing-header-bundled-min.js" | ||
}, | ||
@@ -40,2 +44,3 @@ "repository": { | ||
"finalhandler": "^0.3.4", | ||
"jshint": "^2.6.3", | ||
"mocha": "^2.2.1", | ||
@@ -46,6 +51,7 @@ "mocha-multi": "^0.6.0", | ||
"selenium-webdriver": "^2.45.1", | ||
"serve-static": "^1.9.2" | ||
"serve-static": "^1.9.2", | ||
"uglify-js": "^2.4.19" | ||
}, | ||
"dependencies": { | ||
"jank-free-onscroll": "^0.2.1" | ||
"onscrolling": "^0.3.0" | ||
}, | ||
@@ -52,0 +58,0 @@ "jspm": { |
# Affixing Header [![Build Status](https://travis-ci.org/acusti/affixing-header.svg?branch=master)](https://travis-ci.org/acusti/affixing-header) | ||
Create an affixing header that behaves normally as a user navigates down a page, but reveals itself naturally when a user scrolls or drags upwards. Inspired by iOS Safari, Medium, and others. See an [example implementation][acusti.ca]. Works on desktop and mobile browsers. | ||
Create an affixing header that behaves normally as a user navigates down a page, but reveals itself naturally when a user scrolls or drags upwards. Inspired by iOS Safari, Medium, and others. See [an example implementation][acusti.ca] to see what it’s about. Works on desktop and mobile browsers. | ||
@@ -9,4 +9,11 @@ [![Sauce Test Status](https://saucelabs.com/browser-matrix/acusti.svg)](https://saucelabs.com/u/acusti) | ||
The object exports an `affixing-header` module if being used with a CommonJS or AMD module loader, or else exposes a global object as `window.affixingHeader`. | ||
The module itself is available in a wide range of flavors: | ||
1. As a CommonJS (Browserify-friendly) module (via [UMD]): `dist/affixing-header.js` | ||
2. As an AMD (RequireJS-friendly) module (also via UMD): `dist/affixing-header.js` | ||
3. As the `window.affixingHeader` global with dependencies bundled in: `dist/affixing-header-bundled.js`, or minified as `dist/affixing-header-bundled-min.js` | ||
4. As an ES6/ES2015 module, compatible with ES6-compatible module loaders like [SystemJS][] or compilers like [Babel][]: `src/affixing-header.js` | ||
It exports a single function via `require('affixing-header')` if being used with a CommonJS or AMD module loader, or else exposes the function as a global named `window.affixingHeader`. | ||
### `affixingHeader( element )` | ||
@@ -16,7 +23,7 @@ | ||
The DOM element to which the affixing behaviour should be attached. Must be a single Element (e.g., the result of `document.querySelector` or `document.getElementById`), not a `NodeList`. | ||
The DOM element to which the affixing behavior should be attached. Must be a single Element (e.g., the result of `document.querySelector` or `document.getElementById`), not a `NodeList`. | ||
### Dependencies | ||
This package uses [jank-free onscroll][], a requestAnimationFrame-based, performant, mobile-friendly scroll event handler, to listen for page scrolls, but has no other dependencies. | ||
This package uses [onscrolling][], a requestAnimationFrame-based, performant, mobile-friendly scroll event handler, to listen for page scrolls, but has no other dependencies. | ||
@@ -28,4 +35,7 @@ ### Compatibility | ||
[acusti.ca]: http://www.acusti.ca | ||
[jank-free onscroll]: https://github.com/acusti/jank-free-onscroll | ||
[UMD]: https://github.com/umdjs/umd | ||
[SystemJS]: https://github.com/systemjs/systemjs | ||
[Babel]: https://babeljs.io | ||
[onscrolling]: https://github.com/acusti/onscrolling | ||
[raf-caniuse]: http://caniuse.com/#feat=requestanimationframe | ||
[raf-polyfill]: https://gist.github.com/paulirish/1579671 |
'use strict'; | ||
import onscroll from '../node_modules/jank-free-onscroll/src/onscroll'; | ||
import onscrolling from 'onscrolling'; | ||
@@ -93,4 +93,4 @@ // Keep track of: | ||
header.style.position = 'absolute'; | ||
// Use onscroll helper to listen for scroll changes | ||
onscroll(handleScroll); | ||
// Use onscrolling helper to listen for scroll changes | ||
onscrolling(handleScroll); | ||
} |
@@ -14,3 +14,3 @@ // Test runner | ||
// {name: 'ipad', version: 8}, | ||
{name: 'iphone', version: 8}, | ||
// {name: 'iphone', version: 8}, | ||
{name: 'internet explorer'} | ||
@@ -17,0 +17,0 @@ ); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
45258
18
712
39
15
+ Addedonscrolling@^0.3.0
+ Addedonscrolling@0.3.3(transitive)
- Removedjank-free-onscroll@^0.2.1
- Removedjank-free-onscroll@0.2.2(transitive)