Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save funkatron/4360005 to your computer and use it in GitHub Desktop.
Save funkatron/4360005 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<title>hi</title>
<style type="text/css" media="screen">
#table {
height:300px;
width:200px;
overflow: auto;
border:1px solid #999;
}
.row {
position: relative;
padding:5px;
height:20px;
}
.row.even {
background-color: #CCC;
}
.row.odd {
background-color: #FFFFCC;
}
</style>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.3/underscore-min.js" type="text/javascript" charset="utf-8"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.string/2.3.0/underscore.string.min.js" type="text/javascript" charset="utf-8"></script>
<script src="" type="text/javascript" charset="utf-8"></script>
<script>
(function() {
var requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
var Scroller = function() {
return {
data_rows: [],
element_rows: [],
container_sel: '#table',
$container: null,
row_class_name: "row",
row_generator: function() {
return document.createElement('div');
},
row_class_generator: function(i) {
return i % 2 === 0 ? 'even' : 'odd';
},
$scroller: null,
$row_container: null,
row_count: 100000,
row_height: 30,
num_row_elements: 0,
margin_elements: 5,
current_start_index: 0,
current_stop_index: 20,
initialize: function() {
this.$container = $(this.container_sel);
this.data_rows = this.generateRows(this.row_count);
this.injectScroller();
this.injectRowContainer();
this.bindScrollListener();
this.initRender();
// trigger an initial scroll
this.$container.trigger('scroll');
},
calcScrollerHeight: function(rows, row_height) {
return rows.length * row_height;
},
injectScroller: function() {
var height = this.calcScrollerHeight(this.data_rows, this.row_height);
$scroller = $('<div class="scroller"></div>');
$scroller.css({'height': height});
this.$container.append($scroller);
this.$scroller = $scroller;
},
injectRowContainer: function() {
$row_container = $('<div class="row-container"></div>');
$row_container.css({
'position':'relative'
});
this.$scroller.append($row_container);
this.$row_container = $row_container;
},
updateRowContainerPosition: function(top) {
this.$row_container.css({'top': top});
},
generateRows: function(row_count) {
var rows = [];
for (var i = 0; i < row_count; i++) {
rows.push({
'index': i,
'data': "row " + i.toString()
});
};
return rows;
},
bindScrollListener: function() {
this.$container
.on('scroll', _.bind(this.onScroll, this));
},
_isFirstVisible: function($rows) {
return this._elementInViewport($rows[0], this.$container[0]);
},
_isLastVisible: function($rows) {
return this._elementInViewport($rows[$rows.length-1], this.$container[0]);
},
_removeNotVisibleElements: function() {
},
_onFirstVisible: function() {
if (this.current_start_index > 0) {
// console.log("Delete invisible elements from bottom")
// console.log("Prepend elements to top")
}
},
_onLastVisible: function() {
if (this.current_stop_index < this.data_rows.length) {
// console.log("Delete invisible elements from top")
// console.log("Append elements to end")
}
},
onScroll: function(e) {
// get current viewport
var viewport_top = this.$container.scrollTop();
var viewport_bottom = this.$container.scrollTop() + this.$container.height();
// determine first visible
var first_visible = parseInt(viewport_top/this.row_height, 10);
var last_visible = parseInt(viewport_bottom/this.row_height, 10) + 1;
var slide = viewport_top/this.row_height - first_visible;
if (last_visible <= this.data_rows.length) {
this.updateRowContainerPosition(viewport_top);
this.reRender(first_visible, last_visible, slide);
} else {
// we've probably scrolled all the way to the bottom, so
// push the row_container to the bottom and re-render
// the last set of rows
var row_container_top = this.$scroller.height() - this.$row_container.height()
this.updateRowContainerPosition(row_container_top);
this.reRender(first_visible-1, last_visible-1, 0);
}
$rows = this.$row_container.children();
if (this._isFirstVisible($rows)) {
this._onFirstVisible();
}
if (this._isLastVisible($rows)) {
this._onLastVisible();
}
},
_elementInViewport: function(el, container) {
var el_rect = el.getBoundingClientRect();
var container_rect = container.getBoundingClientRect();
return (
el_rect.bottom >= container_rect.top &&
el_rect.left >= container_rect.left &&
el_rect.top <= container_rect.bottom &&
el_rect.right <= container_rect.right
)
},
_getRenderSet: function(start, stop) {
return this.data_rows.slice(start, stop);
},
_renderRows: function() {
// how many rows should we generate?
this.num_row_elements = parseInt((this.$container.height()/this.row_height), 10) + 1;
this.$row_container.append(_.times(this.num_row_elements, this.row_generator));
},
_putDataInRows: function(start, end, slide) {
start = start || 0,
end = end || this.num_row_elements;
slide = slide || 0;
// update these to track
this.current_start_index = start;
this.current_stop_index = end;
var to_render = this._getRenderSet(this.current_start_index,
this.current_stop_index);
$rows = this.$row_container.children();
var self = this;
$rows.each(function(i) {
$(this)
.html(to_render[i].data)
.attr('class', self.row_class_name)
.addClass(self.row_class_generator(to_render[i].index));
});
$rows.css({top: -Math.floor(slide * this.row_height) + 'px'});
},
initRender: function() {
this._renderRows();
this._putDataInRows();
},
reRender: function(start, end, slide) {
this._putDataInRows(start, end, slide);
}
};
}
var scr = new Scroller();
$(document).ready(function(){
scr.initialize();
});
})();
</script>
</head>
<body>
<div id="table"></div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment