最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Use Intersection Observer to add different classes to elements when scrolled into view from top or bottom - Stack O

programmeradmin5浏览0评论

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.

So far the code works like this:

  • When an element is scrolled into the viewport, the class "inview" is added.
  • When an element is scrolled out of the viewport, the class "inview" is removed.

So far so good. But what I am trying to achieve is this:

Scrolling into view:

  • When an element is scrolled into the viewport from the bottom of the page, the class "inview-bottom" is added.
  • When an element is scrolled into the viewport from the top of the page, the class "inview-top" is added.

Scrolling out of view:

  • When an element is scrolled out of the viewport from the bottom of the page, the class "outview-bottom" is added.
  • When an element is scrolled out of the viewport from the top of the page, the class "outview-top" is added.

Cleaning up:

  • When an element is scrolled into the viewport from the top or bottom of the page, all "outview-*" classes should be removed.
  • When an element is scrolled out of the viewport from the top or bottom of the page, all "inview-*" classes should be removed.

It was suggested in a ment to use the Intersection Observer API and after reading more about it, I believe it presents the best approach to fulfill the requirements.

Here is my code (open in full page - the preview doesn't work well). You can also find the same code on jsFiddle.

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 >= offsetTop && rect.top <= 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"
});
.box {
  width: 100%;
  height: 50vh;
  margin-bottom: 10px;
  background: blue;
  opacity: 0;
  transition: opacity .2s ease;
}

.inview {
  opacity: 1;
}
<script src=".2.4/jquery.min.js"></script>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>

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.

So far the code works like this:

  • When an element is scrolled into the viewport, the class "inview" is added.
  • When an element is scrolled out of the viewport, the class "inview" is removed.

So far so good. But what I am trying to achieve is this:

Scrolling into view:

  • When an element is scrolled into the viewport from the bottom of the page, the class "inview-bottom" is added.
  • When an element is scrolled into the viewport from the top of the page, the class "inview-top" is added.

Scrolling out of view:

  • When an element is scrolled out of the viewport from the bottom of the page, the class "outview-bottom" is added.
  • When an element is scrolled out of the viewport from the top of the page, the class "outview-top" is added.

Cleaning up:

  • When an element is scrolled into the viewport from the top or bottom of the page, all "outview-*" classes should be removed.
  • When an element is scrolled out of the viewport from the top or bottom of the page, all "inview-*" classes should be removed.

It was suggested in a ment to use the Intersection Observer API and after reading more about it, I believe it presents the best approach to fulfill the requirements.

Here is my code (open in full page - the preview doesn't work well). You can also find the same code on jsFiddle.

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 >= offsetTop && rect.top <= 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"
});
.box {
  width: 100%;
  height: 50vh;
  margin-bottom: 10px;
  background: blue;
  opacity: 0;
  transition: opacity .2s ease;
}

.inview {
  opacity: 1;
}
<script src="https://cdnjs.cloudflare./ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>

Share Improve this question edited May 17, 2020 at 15:24 doğukan 27.7k13 gold badges63 silver badges75 bronze badges asked May 15, 2020 at 8:33 Jascha GoltermannJascha Goltermann 1,1244 gold badges16 silver badges35 bronze badges 3
  • You should really use Intersection Observer for this. This was designed to watch elements whenever they e into view and react to it. – cloned Commented May 15, 2020 at 10:05
  • Hi, thanks for the tip! I have really no idea what that is and how to use this. I was only able to put together the code above with a lot of help from others - I wouldn't be able to write or understand it myself.. Can you show me? – Jascha Goltermann Commented May 15, 2020 at 11:15
  • I have found this but I can't make it work for my code: jsfiddle/sublines/xcyaks4g/5 – Jascha Goltermann Commented May 15, 2020 at 11:36
Add a ment  | 

1 Answer 1

Reset to default 5 +50

The fiddle you provided works fine with few changes. You need to apply the observer to all elements for it to work.

See this example:

const config = {
  root: null,
  rootMargin: '0px',
  threshold: [0.1, 0.5, 0.7, 1]
};

let previousY = 0;
let previousRatio = 0;


let observer = new IntersectionObserver(function(entries) {
  entries.forEach(entry => {
    const currentY = entry.boundingClientRect.y
    const currentRatio = entry.intersectionRatio
    const isIntersecting = entry.isIntersecting
    const element = entry.target;

    element.classList.remove("outview-top", "inview-top", "inview-bottom", "outview-bottom");
    // Scrolling up
    if (currentY < previousY) {
      const className = (currentRatio >= previousRatio) ? "inview-top" : "outview-top";
      element.classList.add(className);

      // Scrolling down
    } else if (currentY > previousY) {
      const className = (currentRatio <= previousRatio) ? "outview-bottom" : "inview-bottom";
      element.classList.add(className);
    }

    previousY = currentY
    previousRatio = currentRatio
  })
}, config);

const images = document.querySelectorAll('.box');
images.forEach(image => {
  observer.observe(image);
});
.box {
  width: 100%;
  height: 50vh;
  margin-bottom: 10px;
  background: lightblue;
  transition: opacity .2s ease;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 3em;
}

[class*='inview'] {
  opacity: 1;
}

[class*='outview'] {
  opacity: 0.6;
}
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论