Knockout Performance

by bakhshi

HTML

<script src="//knockoutjs.com/downloads/knockout-3.3.0.js"></script>
<div class="container">
    <div class="prefix"></div>
    <div class="rows" data-bind="foreach:data.slice(index(), index() + count)">
        <div class="row">
            <span data-bind="text:name"></span>@
            <date data-bind="text:Date()"></date>
        </div>
    </div>
    <div class="postfix""></div>
</div>

CSS

.container {
    border: solid 1px blue;
    height: 500px;
    overflow-y: auto;
}
.rows {
    font-size:0;
}
.row {
    font-size:14px;
}

date{
    font-weight:bold;
}

JavaScript

var data = [];
var $ = document.querySelectorAll.bind(document);
var container = $('.container')[0];

for (var index = 0; index < 10000000; index++)
    data.push({
    name: 'reza ' + index
});

console.log(data.length);

const rowHeight = 25;

var model = {
    data: ko.observableArray(data),
    index: ko.observable(0),
    postfixHeight: function () {
        return rowHeight * (this.data().length - (this.index() + this.count)).toFixed() + 'px';
    },
    prefixHeight: function () {
        var h = ( rowHeight * this.index()).toFixed() + 'px';
        console.log(h);
        return h;
    },
    count: 100
};

ko.applyBindings(model, container);


container.addEventListener('scroll', Throttle(function () {
    const add = ((container.scrollTop / rowHeight) | 0);
    console.log('rows: ', add);
    model.index(Math.min(model.data().length - model.count, add));
    $('.prefix')[0].style.height = model.prefixHeight();
    $('.postfix')[0].style.height = model.postfixHeight();
}, 50));

function Throttle(fn, timeout) {
    if (timeout === 0) return fn;
    var result = function throttled() {
        var _arguments = [].slice.call(arguments);

        function CheckForNewCall() {
            return !fn.__hasPendingCall;
        }

        function RescheduleCall() {
            fn.__hasPendingCall = true;
            fn.__lastArguments = _arguments;
            fn.__pendingCall = setTimeout(function () {
                var argArray = fn.__lastArguments.slice(0);
                fn.__lastArguments = null;
                fn.__hasPendingCall = false;
                fn.apply(null, argArray);
            }, timeout);
        }
        if (CheckForNewCall()) RescheduleCall();
        else fn.__lastArguments = _arguments;
    };
    return result;
}