title: Use Intersection Observer to add different classes to elements when scrolled into view from top or bottom
body: <p>I have a piece of jQuery code that adds a css class to elements when they are scrolled into the viewport and removes that class when they are scrolled out of the viewport. </p>
  
  <p>So far the code works like this: </p>
  
  <ul>
  <li>When an element is scrolled into the viewport, the class "inview" is added. </li>
  <li>When an element is scrolled out of the viewport, the class "inview" is removed.</li>
  </ul>
  
  <hr>
  
  <p>So far so good. But what I am trying to achieve is this: </p>
  
  <p><strong>Scrolling into view:</strong></p>
  
  <ul>
  <li>When an element is scrolled into the viewport from the bottom of the page, the class "inview-bottom" is added. </li>
  <li>When an element is scrolled into the viewport from the top of the page, the class "inview-top" is added. </li>
  </ul>
  
  <p><strong>Scrolling out of view:</strong></p>
  
  <ul>
  <li>When an element is scrolled out of the viewport from the bottom of the page, the class "outview-bottom" is added. </li>
  <li>When an element is scrolled out of the viewport from the top of the page, the class "outview-top" is added. </li>
  </ul>
  
  <p><strong>Cleaning up:</strong></p>
  
  <ul>
  <li>When an element is scrolled into the viewport from the top or bottom of the page, all "outview-*" classes should be removed.</li>
  <li>When an element is scrolled out of the viewport from the top or bottom of the page, all "inview-*" classes should be removed.</li>
  </ul>
  
  <hr>
  
  <p>It was suggested in a comment to use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API" rel="nofollow noreferrer">Intersection Observer API</a> and after reading more about it, I believe it presents the best approach to fulfill the requirements.</p>
  
  <p>Here is my code (open in full page - the preview doesn't work well). You can also find <a href="https://jsfiddle.net/sublines/L6fxmq4w/4/" rel="nofollow noreferrer">the same code on jsFiddle</a>.</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>function inView(opt) {
    if (opt.selector === undefined) {
      console.log('Valid selector required for inView');
      return false;
    }
    var elems = [].slice.call(document.querySelectorAll(opt.selector)),
      once = opt.once === undefined ? true : opt.once,
      offsetTop = opt.offsetTop === undefined ? 0 : opt.offsetTop,
      offsetBot = opt.offsetBot === undefined ? 0 : opt.offsetBot,
      count = elems.length,
      winHeight = 0,
      ticking = false;
  
    function update() {
      var i = count;
      while (i--) {
        var elem = elems[i],
          rect = elem.getBoundingClientRect();
        if (rect.bottom &gt;= offsetTop &amp;&amp; rect.top &lt;= winHeight - offsetBot) {
          elem.classList.add('inview');
          if (once) {
            count--;
            elems.splice(i, 1);
          }
        } else {
          elem.classList.remove('inview');
        }
      }
      ticking = false;
    }
  
    function onResize() {
      winHeight = window.innerHeight;
      requestTick();
    }
  
    function onScroll() {
      requestTick();
    }
  
    function requestTick() {
      if (!ticking) {
        requestAnimationFrame(update);
        ticking = true;
      }
    }
    window.addEventListener('resize', onResize, false);
    document.addEventListener('scroll', onScroll, false);
    document.addEventListener('touchmove', onScroll, false);
    onResize();
  }
  inView({
    selector: '.viewme', // an .inview class will get toggled on these elements
    once: false, // set this to false to have the .inview class be toggled on AND off
    offsetTop: 180, // top threshold to be considered "in view"
    offsetBot: 100 // bottom threshold to be considered "in view"
  });</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>.box {
    width: 100%;
    height: 50vh;
    margin-bottom: 10px;
    background: blue;
    opacity: 0;
    transition: opacity .2s ease;
  }
  
  .inview {
    opacity: 1;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"&gt;&lt;/script&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;
  &lt;div class="box viewme"&gt;&lt;/div&gt;</code></pre>
  </div>
  </div>
  </p>