将滚动时的活动状态更改为视口

时间:2018-11-06 08:11:53

标签: javascript jquery html

我正在尝试创建一个静态网站,当的一个div子项进入视口时(准确地,当div元素进入视口的上50%时),该网站会将side-nav中相应的div的类更改为“活性”。它应该可以上下滚动。

到目前为止,我已经从SO上的其他线程尝试了几种解决方案,但均未成功。我认为我一直在解决这个错误。

$(window).on('scroll', function() {
  $("#vars-args").each(function() {
  if (elementInViewport2($(this))) {
    $(this).find("#div1a").addClass("active");
  }
});
});

function elementInViewport2(el) {
  var top = el.offsetTop;
  var left = el.offsetLeft;
  var width = el.offsetWidth;
  var height = el.offsetHeight;

  while (el.offsetParent) {
    el = el.offsetParent;
    top += el.offsetTop;
    left += el.offsetLeft;
  }

  return (
    top < (window.pageYOffset + window.innerHeight) &&
    left < (window.pageXOffset + window.innerWidth) &&
    (top + height) > window.pageYOffset &&
    (left + width) > window.pageXOffset
  );
}
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
<div id="side-nav">
  <a href="" id="div1a">1</a>
  <a href="" id="div2a">2</a>
  <a href="" id="div3a">3</a>
  <a href="" id="div4a">4</a>
  <a href="" id="div5a">5</a>
  <a href="" id="div6a">6</a>
</div>
<div id="content">
  <div id="div1">
    <!--content-->
  </div>
  <div id="div2">
    <!--content-->
  </div>
  <div id="div3">
    <!--content-->
  </div>
  <div id="div4">
    <!--content-->
  </div>
  <div id="div5">
    <!--content-->
  </div>
  <div id="div6">
    <!--content-->
  </div>
</div>

还要注意,内部每个div的内容都可以大于视口的大小。

我在使JavaScript正常工作时遇到了问题。另外请注意,当前的JS是从其他线程复制的。

3 个答案:

答案 0 :(得分:1)

这可以通过使用IntersectionObserver来实现,如@cloned在注释中所述:https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

要实现这一点,您需要将一个回调函数作为参数传递,一旦isIntersecting true (一个选项)后执行>对象(将阈值设置为元素的50%)和 IntersectionObserver

回调根据条目的 id 将活动类切换到 a 元素。

最后,我们遍历 divs 并使观察者观察它们。

const callback = (entries, observer) => {
entries.forEach(entry => {
  const navItem = document.querySelector('#' + entry.target.id + 'a');
    if (entry.isIntersecting) {
      console.log(navItem.getAttribute('id'));
      navItem.classList.add('active');
    } else {
      navItem.classList.remove('active');
    }
  });
};
const options = {
  threshold: 0.5
};
const observer = new IntersectionObserver(callback, options);
const container = document.getElementById('content');
const targetElements = container.querySelectorAll('div');


targetElements.forEach(element => {
 observer.observe(element);
});

这是一个 JSBin 来演示它https://jsbin.com/riyuhediso/47/edit?html,js,console,output

注意,尽管它证明了它的可行性,但并未针对可能非常重要的性能问题进行剖析,因此我不作保证。

如果您使用的是 Bootstrap ,则可以使用 ScrollSpy lib https://getbootstrap.com/docs/4.1/components/scrollspy/,还有 ScrollMagic 很棒,{{ 3}}

答案 1 :(得分:1)

您需要借助.getBoundingClientRect()筛选出视口中的哪个元素 结帐this

并检查视口(window.innerHeight)一半内是否有顶部和底部内容

我借助过滤器功能找出了内置函数中的目录索引,并设置了相应锚点的.active类。

看看这段代码:

var direction = 0; // a variable to keep track of scrolled position;
$(window).on('scroll', function() {
  // check if window is scrolling up or down;
  if ($(window).scrollTop() > direction) { // if true, window scrolling scrolling down;
    $('#side-nav').find('a').removeClass('active'); // remove active class from all anchors
    $('#side-nav').find('a').eq(
      // .eq() selector helps to find elements with index number, and here we pass a filter to find the content that is within the viewport;
      $('#content').find('div').filter(function(index) {
        return this.getBoundingClientRect().y <= (window.innerHeight / 2) && this.getBoundingClientRect().y + this.getBoundingClientRect().height > window.innerHeight / 2;
      }).index()
    ).addClass('active');
    // update the current scroll position now;
    direction = $(window).scrollTop();

  } else { // if false, window scrolling scrolling up;
    $('#side-nav').find('a').removeClass('active'); // remove active class from all anchors
    $('#side-nav').find('a').eq(
      $('#content').find('div').filter(function(index) {
        return this.getBoundingClientRect().y < (window.innerHeight / 2) && this.getBoundingClientRect().y + this.getBoundingClientRect().height > window.innerHeight / 2;
      }).index()
    ).addClass('active');
    // update the current scroll position now;
    direction = $(window).scrollTop();
  }
});
* {
  margin: 0;
  padding: 0;
}

#side-nav {
  /* feel free to remove or change, only for testing */
  position: fixed;
  width: 100%;
  top: 0;
  padding: 15px;
}

#side-nav a {
  /* feel free to remove, only for testing */
  text-decoration: none;
  color: grey;
  margin-right: 5px;
  font-size: 24px;
  font-weight: bold;
}

#side-nav a.active {
  color: #000;
  /* sets color for the default active class */
}

#content div {
  min-height: 600px;
  background-color: #cecece;
  border-bottom: 1px solid #fff;
  box-sizing: border-box;
  padding: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="side-nav">
  <a href="" id="div1a" class='active'>1</a>
  <!-- set a default class assuming the first one will be in viewport while window loads -->
  <a href="" id="div2a">2</a>
  <a href="" id="div3a">3</a>
  <a href="" id="div4a">4</a>
  <a href="" id="div5a">5</a>
  <a href="" id="div6a">6</a>
</div>
<div id="content">
  <div id="div1">
    <p>One</p>
  </div>
  <div id="div2">
    <p>Two</p>
  </div>
  <div id="div3">
    <p>Three</p>
  </div>
  <div id="div4">
    <p>Four</p>
  </div>
  <div id="div5">
    <p>Five</p>
  </div>
  <div id="div6">
    <p>Six</p>
  </div>
</div>

答案 2 :(得分:0)

我找到了解决此问题的方法。 注意:html side-nav div中的所有链接从id更改为class。 我知道这是一个错误的修复程序,因此,如果可以找到任何优化或更好的解决方案,我将保留此问题。

<script>
        window.onload = function() {
                var vars = document.getElementById('vars-args');
                var als = document.getElementById('aliases')
                $(window).on('scroll', function() {
                $('#content').find('div').each(function(){
                        if (eleInView(this)){
                        var ele = $(this).attr('id');
                        document.getElementsByClassName(ele)[0].classList.add("active");
                        };
                        if (!eleInView(this)){
                        console.log(this);
                        var ele = $(this).attr('id');
                        document.getElementsByClassName(ele)[0].classList.remove("active");
                        }; 
                });
                }
        )};

        function eleInView(el){
                var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
                //console.log(el.getBoundingClientRect().top);
                return (h < el.getBoundingClientRect().bottom && el.getBoundingClientRect().top < h);
        }
</script>          
    <script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
    <div id="side-nav">
    <a href="" class="div1">1</a>
    <a href="" class="div2">2</a>
    <a href="" class="div3">3</a>
    <a href="" class="div4">4</a>
    <a href="" class="div5">5</a>
    <a href="" class="div6">6</a>
    </div>
    <div id="content">
    <div id="div1">
    <!--content-->
    </div>
    <div id="div2">
    <!--content-->
    </div>
    <div id="div3">
    <!--content-->
    </div>
    <div id="div4">
    <!--content-->
    </div>
    <div id="div5">
    <!--content-->
    </div>
    <div id="div6">
    <!--content-->
    </div>
    </div>