Intersection Observer

The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.

Vue Example

data () {
  return {
    sectionObserver: null
  }
},
mounted () {
  this.observeSections()
},
methods: {
  observeSections() {
    try {
      this.sectionObserver.disconnect()
    } catch (error) {}

    const options = {
      rootMargin: '0px 0px',
      threshold: 0
    }
    this.sectionObserver = new IntersectionObserver(this.sectionObserverHandler, options)
  
    // Observe each section
    const sections = document.querySelectorAll('.section')
    sections.forEach(section => {
      this.sectionObserver.observe(section)
    })
  },
  sectionObserverHandler (entries) {
    for (const entry of entries) {
      if (entry.isIntersecting) {
         const sectionId = entry.target.id
         // Push sectionId to router here 
         this.$router.replace({ name: this.$route.name, hash: `#${sectionId}` })
      }
    }
  }
}

Variable threshold

function doTheThing (el) {
    el.classList.add('in-view');
}

const threshold = 0.5;

document.querySelectorAll('section').forEach(el => {
    const elHeight = el.getBoundingClientRect().height;
    var th = threshold;

    // The element is too tall to ever hit the threshold - change threshold
    if (elHeight > (window.innerHeight * threshold)) {
        th = ((window.innerHeight * threshold) / elHeight) * threshold;
    }

    new IntersectionObserver(iEls => iEls.forEach(iEl => doTheThing(iEl)), {threshold: th}).observe(el);
});