Skip to content

Instantly share code, notes, and snippets.

Created January 21, 2016 16:39
Show Gist options
  • Save anonymous/ab57fcc47f92c71d0c0b to your computer and use it in GitHub Desktop.
Save anonymous/ab57fcc47f92c71d0c0b to your computer and use it in GitHub Desktop.
Ember Virtual List Component // source http://emberjs.jsbin.com/qutevisabu
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Virtual List Component</title>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://builds.emberjs.com/tags/v1.13.5/ember-template-compiler.js"></script>
<script src="http://builds.emberjs.com/tags/v1.13.5/ember.debug.js"></script>
<style id="jsbin-css">
/* Put your CSS here */
html, body {
margin: 20px;
box-sizing: border-box;
}
h1, h2 {
font-weight: normal;
}
.x-virtual-list__container {
width: 300px;
height: 500px;
outline: 1px solid black;
overflow: scroll;
position: relative;
}
.x-virtual-list__scroller {
position: absolute;
background-color: blue;
width: 100%;
}
.x-virtual_list__item {
position: absolute;
outline: 1px solid white;
height: 40px;
width: calc(100% - 10px);
line-height: 40px;
padding-left: 10px;
color: white;
background-color: green;
}
</style>
</head>
<body>
<script type="text/x-handlebars">
<h1>Scroll Scroll</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" id="components/x-virtual-list-item">
{{item.name}}
</script>
<script type="text/x-handlebars" id="components/x-virtual-list">
<div class="x-virtual-list__container">
<div class="x-virtual-list__scroller" style={{scrollerStyle}}>
{{#each visibleRows key='rowKey' as |row|}}
<div class="x-virtual_list__item" style={{row.style}}>{{component itemComponent item=row.item}}</div>
{{/each}}
</div>
</div>
<div>
<p>scroll offset: {{scrollOffset}}</p>
<p>visible elements: {{visibleRows.length}}</p>
<p>start offset: {{startOffset}}</p>
<p>end offset: {{endOffset}}</p>
<p>start index: {{startIndex}}</p>
<p>end index: {{endIndex}}</p>
</div>
</script>
<script type="text/x-handlebars" data-template-name="index">
{{x-virtual-list items=model}}
</script>
<script id="jsbin-javascript">
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
var people = [
'Bill',
'Zach',
'Seung',
'Kapil',
'Tom',
'Ren',
'George',
'Ed',
'Ben',
'Curtis',
'Allie',
'Tao',
'David',
'Gian',
'Clifton',
'Richard',
'Max',
'Tony',
'Kiran',
'Mike',
'Brian',
'Joe',
'Matt',
'Alex',
'Jeremy',
'Travis'
];
var items = [];
for (var i = 0; i < 1000; i++) {
var person = people[Math.floor(Math.random() * people.length)];
items.push({
name: (i+1) + ". " + person
});
}
return items;
}
});
App.XVirtualListComponent = Ember.Component.extend({
rowHeight: 40,
scrollOffset: 0,
containerHeight: 0,
itemComponent: 'x-virtual-list-item',
visibleRows: function() {
var rowHeight = this.get('rowHeight');
var startIndex = this.get('startIndex');
var endIndex = this.get('endIndex');
return this.get('items').slice(startIndex, endIndex).map(function(item, i) {
var index = i + startIndex;
var top = (rowHeight * index);
return {
rowKey: index,
item: item,
style: new Ember.Handlebars.SafeString("top: " + top + "px;")
};
});
}.property('items', 'rowHeight', 'startIndex', 'endIndex'),
startOffset: function() {
return Math.abs(this.get('scrollOffset')) / this.get('rowHeight');
}.property('scrollOffset', 'containerHeight', 'rowHeight'),
endOffset: function() {
return (Math.abs(this.get('scrollOffset')) + this.getWithDefault('containerHeight', 0)) / this.get('rowHeight');
}.property('scrollOffset', 'containerHeight', 'rowHeight'),
startIndex: function() {
return Math.floor(this.get('startOffset'));
}.property('startOffset'),
endIndex: function() {
return Math.ceil(this.get('endOffset'));
}.property('endOffset'),
scrollerStyle: function() {
var height = this.get('items.length') * this.get('rowHeight');
var style = "height: " + height + "px";
return new Ember.Handlebars.SafeString(style);
}.property('items.length', 'rowHeight'),
hookupScroll: function() {
var component = this;
this.outer().bind('scroll', function() {
Ember.run.once(component, function() {
var containerOffset = this.outer().offset().top;
var scrollerOffset = this.inner().offset().top;
var scrollOffset = scrollerOffset - containerOffset;
this.set('scrollOffset', scrollOffset);
});
});
}.on('didInsertElement'),
readySetGo: function() {
Ember.run.later(this, function() {
this.set('containerHeight', this.outer().height());
});
}.on('didInsertElement'),
outer: function() {
return this.$().find('.x-virtual-list__container');
},
inner: function() {
return this.$().find('.x-virtual-list__scroller');
}
});
</script>
<script id="jsbin-source-html" type="text/html"><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Virtual List Component</title>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"><\/script>
<script src="http://builds.emberjs.com/tags/v1.13.5/ember-template-compiler.js"><\/script>
<script src="http://builds.emberjs.com/tags/v1.13.5/ember.debug.js"><\/script>
</head>
<body>
<script type="text/x-handlebars">
<h1>Scroll Scroll</h1>
{{outlet}}
<\/script>
<script type="text/x-handlebars" id="components/x-virtual-list-item">
{{item.name}}
<\/script>
<script type="text/x-handlebars" id="components/x-virtual-list">
<div class="x-virtual-list__container">
<div class="x-virtual-list__scroller" style={{scrollerStyle}}>
{{#each visibleRows key='rowKey' as |row|}}
<div class="x-virtual_list__item" style={{row.style}}>{{component itemComponent item=row.item}}</div>
{{/each}}
</div>
</div>
<div>
<p>scroll offset: {{scrollOffset}}</p>
<p>visible elements: {{visibleRows.length}}</p>
<p>start offset: {{startOffset}}</p>
<p>end offset: {{endOffset}}</p>
<p>start index: {{startIndex}}</p>
<p>end index: {{endIndex}}</p>
</div>
<\/script>
<script type="text/x-handlebars" data-template-name="index">
{{x-virtual-list items=model}}
<\/script>
</body>
</html>
</script>
<script id="jsbin-source-css" type="text/css">/* Put your CSS here */
html, body {
margin: 20px;
box-sizing: border-box;
}
h1, h2 {
font-weight: normal;
}
.x-virtual-list__container {
width: 300px;
height: 500px;
outline: 1px solid black;
overflow: scroll;
position: relative;
}
.x-virtual-list__scroller {
position: absolute;
background-color: blue;
width: 100%;
}
.x-virtual_list__item {
position: absolute;
outline: 1px solid white;
height: 40px;
width: calc(100% - 10px);
line-height: 40px;
padding-left: 10px;
color: white;
background-color: green;
}</script>
<script id="jsbin-source-javascript" type="text/javascript">App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
var people = [
'Bill',
'Zach',
'Seung',
'Kapil',
'Tom',
'Ren',
'George',
'Ed',
'Ben',
'Curtis',
'Allie',
'Tao',
'David',
'Gian',
'Clifton',
'Richard',
'Max',
'Tony',
'Kiran',
'Mike',
'Brian',
'Joe',
'Matt',
'Alex',
'Jeremy',
'Travis'
];
var items = [];
for (var i = 0; i < 1000; i++) {
var person = people[Math.floor(Math.random() * people.length)];
items.push({
name: (i+1) + ". " + person
});
}
return items;
}
});
App.XVirtualListComponent = Ember.Component.extend({
rowHeight: 40,
scrollOffset: 0,
containerHeight: 0,
itemComponent: 'x-virtual-list-item',
visibleRows: function() {
var rowHeight = this.get('rowHeight');
var startIndex = this.get('startIndex');
var endIndex = this.get('endIndex');
return this.get('items').slice(startIndex, endIndex).map(function(item, i) {
var index = i + startIndex;
var top = (rowHeight * index);
return {
rowKey: index,
item: item,
style: new Ember.Handlebars.SafeString("top: " + top + "px;")
};
});
}.property('items', 'rowHeight', 'startIndex', 'endIndex'),
startOffset: function() {
return Math.abs(this.get('scrollOffset')) / this.get('rowHeight');
}.property('scrollOffset', 'containerHeight', 'rowHeight'),
endOffset: function() {
return (Math.abs(this.get('scrollOffset')) + this.getWithDefault('containerHeight', 0)) / this.get('rowHeight');
}.property('scrollOffset', 'containerHeight', 'rowHeight'),
startIndex: function() {
return Math.floor(this.get('startOffset'));
}.property('startOffset'),
endIndex: function() {
return Math.ceil(this.get('endOffset'));
}.property('endOffset'),
scrollerStyle: function() {
var height = this.get('items.length') * this.get('rowHeight');
var style = "height: " + height + "px";
return new Ember.Handlebars.SafeString(style);
}.property('items.length', 'rowHeight'),
hookupScroll: function() {
var component = this;
this.outer().bind('scroll', function() {
Ember.run.once(component, function() {
var containerOffset = this.outer().offset().top;
var scrollerOffset = this.inner().offset().top;
var scrollOffset = scrollerOffset - containerOffset;
this.set('scrollOffset', scrollOffset);
});
});
}.on('didInsertElement'),
readySetGo: function() {
Ember.run.later(this, function() {
this.set('containerHeight', this.outer().height());
});
}.on('didInsertElement'),
outer: function() {
return this.$().find('.x-virtual-list__container');
},
inner: function() {
return this.$().find('.x-virtual-list__scroller');
}
});</script></body>
</html>
/* Put your CSS here */
html, body {
margin: 20px;
box-sizing: border-box;
}
h1, h2 {
font-weight: normal;
}
.x-virtual-list__container {
width: 300px;
height: 500px;
outline: 1px solid black;
overflow: scroll;
position: relative;
}
.x-virtual-list__scroller {
position: absolute;
background-color: blue;
width: 100%;
}
.x-virtual_list__item {
position: absolute;
outline: 1px solid white;
height: 40px;
width: calc(100% - 10px);
line-height: 40px;
padding-left: 10px;
color: white;
background-color: green;
}
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
var people = [
'Bill',
'Zach',
'Seung',
'Kapil',
'Tom',
'Ren',
'George',
'Ed',
'Ben',
'Curtis',
'Allie',
'Tao',
'David',
'Gian',
'Clifton',
'Richard',
'Max',
'Tony',
'Kiran',
'Mike',
'Brian',
'Joe',
'Matt',
'Alex',
'Jeremy',
'Travis'
];
var items = [];
for (var i = 0; i < 1000; i++) {
var person = people[Math.floor(Math.random() * people.length)];
items.push({
name: (i+1) + ". " + person
});
}
return items;
}
});
App.XVirtualListComponent = Ember.Component.extend({
rowHeight: 40,
scrollOffset: 0,
containerHeight: 0,
itemComponent: 'x-virtual-list-item',
visibleRows: function() {
var rowHeight = this.get('rowHeight');
var startIndex = this.get('startIndex');
var endIndex = this.get('endIndex');
return this.get('items').slice(startIndex, endIndex).map(function(item, i) {
var index = i + startIndex;
var top = (rowHeight * index);
return {
rowKey: index,
item: item,
style: new Ember.Handlebars.SafeString("top: " + top + "px;")
};
});
}.property('items', 'rowHeight', 'startIndex', 'endIndex'),
startOffset: function() {
return Math.abs(this.get('scrollOffset')) / this.get('rowHeight');
}.property('scrollOffset', 'containerHeight', 'rowHeight'),
endOffset: function() {
return (Math.abs(this.get('scrollOffset')) + this.getWithDefault('containerHeight', 0)) / this.get('rowHeight');
}.property('scrollOffset', 'containerHeight', 'rowHeight'),
startIndex: function() {
return Math.floor(this.get('startOffset'));
}.property('startOffset'),
endIndex: function() {
return Math.ceil(this.get('endOffset'));
}.property('endOffset'),
scrollerStyle: function() {
var height = this.get('items.length') * this.get('rowHeight');
var style = "height: " + height + "px";
return new Ember.Handlebars.SafeString(style);
}.property('items.length', 'rowHeight'),
hookupScroll: function() {
var component = this;
this.outer().bind('scroll', function() {
Ember.run.once(component, function() {
var containerOffset = this.outer().offset().top;
var scrollerOffset = this.inner().offset().top;
var scrollOffset = scrollerOffset - containerOffset;
this.set('scrollOffset', scrollOffset);
});
});
}.on('didInsertElement'),
readySetGo: function() {
Ember.run.later(this, function() {
this.set('containerHeight', this.outer().height());
});
}.on('didInsertElement'),
outer: function() {
return this.$().find('.x-virtual-list__container');
},
inner: function() {
return this.$().find('.x-virtual-list__scroller');
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment