ScrollPosition: Finished
by kirbysayshi
HTML
<button data-bind="click: unshift">Unshift</button>
<button data-bind="click: push">Push</button>
<label>Monitor scroll events: <input type="checkbox" data-bind="checked: addOnScroll" /></label>
<div class="viewport" data-bind="foreach: { data: things }, event: { scroll: onScroll }">
<div data-bind="style: $data"></div>
</div>
CSS
html, body { margin: 0; padding: 0; height: 100% }
.viewport {
height: 300px;
overflow-y: scroll;
border: 1px solid hsl(0, 100%, 5%);
}
.viewport div {
height: 40px;
}
JavaScript
function ScrollPosition(node) {
this.node = node;
this.previousScrollHeightMinusTop = 0;
this.readyFor = 'up';
}
ScrollPosition.prototype.restore = function () {
if (this.readyFor === 'up') {
this.node.scrollTop = this.node.scrollHeight - this.previousScrollHeightMinusTop;
}
// 'down' doesn't need to be special cased unless the
// content was flowing upwards, which would only happen
// if the container is position: absolute, bottom: 0 for
// a Facebook messages effect
}
ScrollPosition.prototype.prepareFor = function (direction) {
this.readyFor = direction || 'up';
this.previousScrollHeightMinusTop = this.node.scrollHeight - this.node.scrollTop;
}
function Model() {
var self = this;
var elViewport = document.querySelector('.viewport');
self.things = ko.observableArray([
randBackColor(),
randBackColor(),
randBackColor(),
randBackColor(),
randBackColor(),
randBackColor(),
randBackColor(),
randBackColor(),
randBackColor(),
randBackColor()]);
self.scrollPosition = new ScrollPosition(elViewport);
self.scrollPosition.prepareFor('top');
self.unshift = function () {
self.scrollPosition.prepareFor('up');
setTimeout(function () {
self.things.unshift(randBackColor());
self.scrollPosition.restore();
}, 1000)
}
self.push = function () {
self.scrollPosition.prepareFor('down');
setTimeout(function () {
self.things.push(randBackColor());
self.scrollPosition.restore();
}, 1000)
}
self.addOnScroll = ko.observable(false);
self.onScroll = function() {
if (!self.addOnScroll()) return;
if (elViewport.scrollTop === 0) {
self.unshift();
}
if (elViewport.scrollTop + elViewport.clientHeight === elViewport.scrollHeight) {
self.push();
...