Vue

by Michel Beloshitsky

HTML

<div id="app">
  <h2>Todos:</h2>
  <ol v-uberscroll:24="todos">
    <div>
      <li v-for="todo in todos.window">
        {{ todo.index }}. 
        <label>
          <input type="checkbox"
            v-on:change="toggle(todo)"
            v-bind:checked="todo.done">

          <del v-if="todo.done">
            {{ todo.text }}
          </del>
          <span v-else>
            {{ todo.text }}
          </span>
        </label>
      </li>
    </div>
  </ol>
</div>

CSS

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

#app ol {
  height: 200px;
  overflow-y: auto;
}

li {
  height: 24px;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}

Vue

Vue.directive('uberscroll', {
    inserted (el, { value, arg }) {
        const itemHeight = Number(arg)

        const updateWindow = () => {
            value.offset = Math.ceil(el.scrollTop / itemHeight)
            value.limit = Math.ceil(el.clientHeight / itemHeight) * 2
            value.window = value.in
                .slice(value.offset, value.offset + value.limit)
                .map((item, index) => ({ ...item, index: value.offset + index }))
            wrapper.style.paddingTop = (value.offset * itemHeight) + 'px'
            wrapper.style.paddingBottom = ((value.in.length - (value.offset + value.limit)) * itemHeight) + 'px'
        }

        el.classList.add('uberscroll')
        const wrapper = el.firstChild
        el.addEventListener('scroll', updateWindow)
        updateWindow()
    }
})

const times = (n, arr) => n === 0 ? [] : arr.concat(times(n - 1, arr))

new Vue({
  el: "#app",
  data: {
     todos: {
       in:  times(1000, [
         { text: "Learn JavaScript", done: false },
         { text: "Learn Vue", done: false },
         { text: "Play around in JSFiddle", done: true },
         { text: "Build something awesome", done: true }
       ]),
       limit: 0, 
       offset: 0,
       window: []
     }
  },
  methods: {
  	toggle: function(todo){
    	todo.done = !todo.done
    }
  }
})