Skip to content

Instantly share code, notes, and snippets.

@abicky
Last active August 29, 2015 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abicky/7c351a6ed1170d9a8c5f to your computer and use it in GitHub Desktop.
Save abicky/7c351a6ed1170d9a8c5f to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=320,user-scalable=no">
<style>
body {
margin: 0;
padding: 0;
height: 3000px;
}
#parent {
margin-top: 600px;
border: solid black 1px;
width: 320px;
}
#header {
color: white;
background: red;
}
#footer {
color: white;
background: blue;
}
.box {
width: 320px;
height: 100px;
border: solid black 1px;
}
</style>
</head>
<body>
下にスクロールしてください
<div id="parent">
<div id="header" class="box">Header</div>
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box" style="display: none;">6</div>
<div class="box" style="display: none;">7</div>
<div class="box" style="display: none;">8</div>
<div class="box" style="display: none;">9</div>
<div class="box" style="display: none;">10</div>
<div id="footer" class="box">Footer</div>
</div>
<script src="https://rawgit.com/abicky/7c351a6ed1170d9a8c5f/raw/de61d7947118a28ca5abbc206b425157adad5240/sticky.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 動的に parent element の高さを変える(auto pager で要素が追加されるケースとか)
setTimeout(function() {
var boxes = document.getElementsByClassName('box');
for (var i = boxes.length - 1; i >= 0; --i) {
boxes[i].style.display = 'block';
}
}, 1000);
sticky(document.getElementById('header'), { top: 10 });
sticky(document.getElementById('footer'), { bottom: 10 });
});
</script>
</body>
</html>
/**
* Copyright 2015- Takeshi Arabiki
* License: MIT License (http://opensource.org/licenses/MIT)
*/
var sticky = (function() {
var INTERVAL = 1000 / 60; // 60 FPS for setTimeout
var STICKY_POSITION = (function() {
var div = document.createElement('div');
div.style.position = 'sticky';
if (div.style.position === 'sticky') {
return 'sticky';
}
div.style.position = '-webkit-sticky';
if (div.style.position === '-webkit-sticky') {
return '-webkit-sticky';
}
})();
var requestAnimationFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
setTimeout;
function isValid(options) {
if (options.top == null && options.bottom == null) {
return false;
}
if (options.top != null && options.bottom != null) {
return false;
}
return true;
}
function tickForTop(stickyElement, spacer, top) {
return function tick() {
var stickyElementHeight = stickyElement.offsetHeight;
var parentHeight = stickyElement.parentElement.offsetHeight;
spacer.style.height = stickyElementHeight + 'px';
var parentOffsetTop = 0;
var parent = spacer.parentElement;
while (parent) {
parentOffsetTop += parent.offsetTop;
parent = parent.offsetParent;
}
var offsetTop = parentOffsetTop + spacer.offsetTop;
var scrollTop = document.body.scrollTop;
if (scrollTop > parentOffsetTop + parentHeight - stickyElementHeight - top) {
stickyElement.style.top = '';
stickyElement.style.bottom = 0;
stickyElement.style.position = 'absolute';
} else if (scrollTop > parentOffsetTop - top) {
stickyElement.style.top = top + 'px';
stickyElement.style.bottom = '';
stickyElement.style.position = 'fixed';
} else {
stickyElement.style.top = 0;
stickyElement.style.bottom = '';
stickyElement.style.position = 'absolute';
}
requestAnimationFrame(tick, INTERVAL);
};
}
function tickForBottom(stickyElement, spacer, bottom) {
return function tick() {
var stickyElementHeight = stickyElement.offsetHeight;
spacer.style.height = stickyElementHeight + 'px';
var parentOffsetTop = 0;
var parent = spacer.parentElement;
while (parent) {
parentOffsetTop += parent.offsetTop;
parent = parent.offsetParent;
}
var offsetTop = parentOffsetTop + spacer.offsetTop;
var windowOffsetBottom = document.body.scrollTop + window.innerHeight;
if (windowOffsetBottom < parentOffsetTop + stickyElementHeight + bottom) {
stickyElement.style.top = 0;
stickyElement.style.bottom = null;
stickyElement.style.position = 'absolute';
} else if (windowOffsetBottom <= offsetTop + stickyElementHeight + bottom) {
stickyElement.style.top = null;
stickyElement.style.bottom = bottom + 'px';
stickyElement.style.position = 'fixed';
} else {
stickyElement.style.top = null;
stickyElement.style.bottom = 0;
stickyElement.style.position = 'absolute';
}
requestAnimationFrame(tick, INTERVAL);
};
}
return function sticky(stickyElement, options) {
if (!isValid(options)) {
throw new Error("'top' or 'bottom', but not both, must be specified");
}
if (STICKY_POSITION) {
stickyElement.style.position = STICKY_POSITION;
if (options.top != null) {
stickyElement.style.top = options.top + 'px';
} else {
stickyElement.style.bottom = options.bottom + 'px';
}
return;
}
var spacer = document.createElement('div');
stickyElement.parentElement.insertBefore(spacer, stickyElement);
if (getComputedStyle(stickyElement.parentElement).position === 'static') {
stickyElement.parentElement.style.position = 'relative';
}
var tick;
if (options.top != null) {
tick = tickForTop(stickyElement, spacer, options.top);
} else {
tick = tickForBottom(stickyElement, spacer, options.bottom);
}
requestAnimationFrame(tick, INTERVAL);
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment