I am developing an application using KnockoutJS which intends to show pages/slides in a long, scrollable list.
Each of these pages has some advanced WYSIWYG capabilities as well as a ton of other tools & data attached.
However, as the number of pages/slides grows, I am noticing huge performance hits.
Therefore, I would like to change to a way that only renders the slides/pages that are in the viewport. Once you scroll past an item, I want KnockoutJS to destroy whatever it rendered before and, instead, render whatever has come into the viewport. Items that are destroyed should be replaced with a placeholder li-tag (until you scroll back to them).
So far so good, I have put together a JSFiddle which nicely shows how only the "visible" items have their observable "inView" set to true. Take a look at the demo showing you how the viewmodel responds to scrolling: https://jsfiddle.net/1xxy6q83/
The issue, however, is to get the conditional display working. Whenever I try to mix/match foreach
and if
, it stops working.
What I am looking for is that Knockout only renders what's seen in the viewport. As an item moves out of view, Knockout should clean it up, remove any events and instead render a very simple placeholder li-tag which ensures the list doesn't jump about.
You can use template
bindings where the template name is based on the value of inView
:
function Page(page) {
var self = this;
this.page = ko.observable(page);
this.inView = ko.observable(false);
this.templateName = function() {
return self.inView() ? 'complex' : 'placeholder';
};
}
The binding looks like:
<ul data-bind="foreach: pages">
<li data-bind="inview: $data">
<div data-bind="template: templateName()">
</div>
</li>
</ul>
And here's an updated fiddle. I put a time delay on the setting of inView
so you can see the template-swapping happening as you scroll.