diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
index 4bcdb82f95a0aacbab432c318c69bd885d6a8e13..2f15b8ae548f3e384da0f7f85f162928ea5f1ae4 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
@@ -58,10 +58,14 @@
     </template>
     <template #default="{ items }">
       <v-list-item
-        v-for="day in groupDocsByDay(items)"
+        v-for="{ date, docs, first, last } in groupDocsByDay(items)"
+        v-intersect="onIntersect"
+        :data-date="date.toISODate()"
+        :data-first="first"
+        :data-last="last"
         two-line
-        :key="'day-' + day[0]"
-        :id="'documentation_' + day[0].toISODate()"
+        :key="'day-' + date"
+        :id="'documentation_' + date.toISODate()"
       >
         <v-list-item-content>
           <v-subheader class="text-h6">{{
@@ -69,7 +73,7 @@
           }}</v-subheader>
           <v-list max-width="100%" class="pt-0 mt-n1">
             <v-list-item
-              v-for="doc in day.slice(1)"
+              v-for="doc in docs"
               :key="'documentation-' + (doc.oldId || doc.id)"
             >
               <documentation-modal
@@ -144,8 +148,9 @@ export default {
   data() {
     return {
       gqlQuery: documentationsForCoursebook,
+      currentDate: "",
+      visible: [],
       knownDates: {},
-      docsByDay: {},
       lastQuery: null,
       dateStart: "",
       dateEnd: "",
@@ -208,7 +213,8 @@ export default {
       }
       // Resetting known dates to dateRange around current date
       this.knownDates = {};
-      const dateRange = this.dateRange(DateTime.fromISO(this.$route.hash.substring(1)))
+      this.currentDate = this.$route.hash.substring(1);
+      const dateRange = this.dateRange(DateTime.fromISO(this.$route.hash.substring(1)));
       dateRange.forEach((ts) => this.knownDates[ts] = true);
       const lastIdx = dateRange.length - 1;
       // Returning a dateRange each around first & last date for the initial query
@@ -234,47 +240,23 @@ export default {
       const docsByDay = docs.reduce((byDay, doc) => {
         // This works with dummy. Does actual doc have dateStart instead?
         const day = DateTime.fromISO(doc.datetimeStart).startOf("day");
-        byDay[day] ??= [day];
-        byDay[day].push(doc);
+        byDay[day] ??= {date: day, docs: [], first: false, last: false};
+        byDay[day]['docs'].push(doc);
         return byDay;
       }, {});
       // => [[dt doc ...] ...]
       return Object.keys(docsByDay)
         .sort()
-        .map((key) => docsByDay[key]);
-      // sorting is necessary since backend can send docs unordered
-    },
-    debounce(fn, delay) {
-      let timer;
-      return () => {
-        console.log('debounce');
-        clearTimeout(timer);
-        timer = setTimeout(fn, delay);
-      }
-    },
-    // Adapted from 
-    // https://github.com/vuejs/vuepress/blob/38e98634af117f83b6a32c8ff42488d91b66f663/packages/%40vuepress/plugin-active-header-links/clientRootMixin.js
-    setCurrentDay() {
-      const days = Array.from(document.querySelectorAll("[id^='documentation_']"));
-
-      const scrollTop = Math.max(
-        window.pageYOffset,
-        document.documentElement.scrollTop,
-        document.body.scrollTop
-      );
-
-      for (let i = 0; i < days.length; i++) {
-        const day = days[i];
-        const nextDay =days[i + 1];
-
-        if ((scrollTop >= day.offsetTop + 10 || i == 0) && (!nextDay || scrollTop < nextDay.offsetTop - 10)) {
-          const date = day.id.split("_")[1];
-          if (date !== this.$route.hash.substring(1)) {
-            this.gotoDate(date);
+        .map((key, i, {length}) => {
+          const day = docsByDay[key];
+          if (i === 0) {
+            day['first'] = true;
+          } else if (i === length - 1) {
+            day['last'] = true;
           }
-          return
-        }
-      }
+          return day;
+        });
+      // sorting is necessary since backend can send docs unordered
     },
     /**
      * @param {"prev"|"next"} direction
@@ -382,10 +364,40 @@ export default {
       this.assureDate(DateTime.fromISO(date));
       // scroll
     },
+    onIntersect(entries, observer) {
+      const entry = entries[0];
+      if (entry.isIntersecting) {
+        // coming
+        console.log('intersect', this.visible);
+
+        // track visible
+        if (this.visible[0] > entry.target.dataset.date || this.visible.length === 0) {
+          // coming is new first (top) date
+          this.visible.unshift(entry.target.dataset.date);
+          console.log('current', this.visible[0]);
+        } else if (this.visible[this.visible.length -1] < entry.target.dataset.date) {
+          // coming is new last (bottom) date
+          this.visible.push(entry.target.dataset.date);
+        } 
+
+        // load more
+        if (entry.target.dataset.first) {
+          console.log('load up');
+        } else if (entry.target.dataset.last) {
+          console.log('load down');
+        }
+      } else if (this.visible[0] === entry.target.dataset.date) {
+        // first (top) visible date is going
+        this.visible.shift()
+        console.log('current', this.visible[0]);
+      } else if (this.visible[this.visible.length - 1] === entry.target.dataset.date) {
+        // last (bottom) visible date is going
+        this.visible.pop()
+      }
+    },
   },
   created() {
     this.resetDate();
-    window.addEventListener('scroll', this.debounce(this.setCurrentDay, 300));
   },
 };
 </script>