Last active
December 10, 2015 01:38
-
-
Save funkatron/4360005 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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