首页 > 解决方案 > 检查元素在视口中是否可见

问题描述

我现在已经尝试了多种方法来做到这一点,我已经完成了 80%,但还没有完全完成。我有不同大小的部分。当我在相应部分时,我试图添加一个活动类的导航,表明它是活动的。

代码有错误的倾向,在 nav-gallery 内,我有一个砖石画廊,还有光滑的滑块,我不确定这是否会影响它。但是活跃的班级倾向于坚持画廊,在导航中。每个部分的高度至少为 100vh。

根据 Cedric 的建议,我尝试使用交叉点观察者 API。但它有同样的问题,至少在我的实现中,它是错误的,并且即使不在视口中,画廊也会以某种方式处于活动状态。

  let options = {
    root: null,
    rootMargin: '0px',
    threshold: 1
  }
  let callback = (entries, observer) => {
      console.log("callback called");
    entries.forEach(entry => {
        console.log("set active for " + entry.target.id);
        let sectionId = entry.target.id;
        navItems.each(function(){
            $(this).removeClass('active');
        });
        $("a[href='#" + sectionId + "']").addClass('active');
    });
  };

  let observer = new IntersectionObserver(callback, options);

  pageSections.each(function () {
    let target = document.querySelector("#" + $(this).attr('id'));
    observer.observe(target);
  });

const navItems = $(".navigation-item");
const pageSections = $(".section-page");

const elementIsInView = el => {
    const scroll = window.scrollY || window.pageYOffset
    const boundsTop = el.getBoundingClientRect().top + scroll

    const viewport = {
        top: scroll,
        bottom: scroll + window.innerHeight,
    }

    const bounds = {
        top: boundsTop,
        bottom: boundsTop + el.clientHeight,
    }

    return ( bounds.bottom >= viewport.top && bounds.bottom <= viewport.bottom ) 
        || ( bounds.top <= viewport.bottom && bounds.top >= viewport.top );
}
$(function () {
    $(window).scroll(function () {
        pageSections.each(function () {
            console.log("elements to check " + $(this).attr('id'))
            if(elementIsInView($(this)[0])){
                console.log("element is in viewport " + $(this).attr('id'))
                // this = the section that is visible
                let sectionId = $(this).attr('id');
                navItems.each(function(){
                    $(this).removeClass('active');
                });
                $("a[href='#" + sectionId + "']").addClass('active');
            }
        })
    })
})   
.section-page{
  height:100vh;
}
.navigation-fixed{
  position:fixed;
  top:20px;
  left:20px;
}
.navigation-fixed ul li a.active{
  color:red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<nav class="navigation-fixed">
        <ul>
            <li>
                <a href="#nav-contact" class="navigation-item">CONTACT</a>
            </li>
            <li>
                <a href="#nav-info" class="navigation-item">INFO</a>
            </li>
            <li>
                <a href="#nav-gallery" class="naviation-item">GALLERY</a>
            </li>
            <li>
                <a href="#nav-home" class="navigation-item active">Home</a>
            </li>
        </ul>
    </nav>
    
    <section class="section-page" id="nav-gallery">Content here</section>
<section class="section-page" id="nav-info">Content here</section>
<section class="section-page" id="nav-contact">Content here</section>

标签: javascriptjqueryviewport

解决方案


这是一个使用Intersection Observer的小/基本示例 我希望这会有所帮助

// list of elements to be observed
const targets = document.getElementsByClassName('section-page')

const options = {
  root: null, // null means root is viewport
  rootMargin: '0px',
  threshold: 0.5 // trigger callback when 50% of the element is visible
}


function callback(entries, observer) { 
  entries.forEach(entry => {

    if(entry.isIntersecting){
    	document.querySelector('.active').classList.remove('active');
      const sectionId = entry.target.id; // identify which element is visible in the viewport at 50%
      document.querySelector(`[href="#${sectionId}"]`).classList.add('active');
      
    }
  });
};

let observer = new IntersectionObserver(callback, options);

[...targets].forEach(target => observer.observe(target));
body {
  margin: 0;
}

main {
  margin: 0;
  display: grid;
  grid-template-columns: 150px 1fr;
  font-family: sans-serif; 
}

#nav {
    list-style-type: none;
}

.navigation-item {
    color: inherit;
    text-decoration: none;
    display: block;
    margin-bottom: 12px;
    padding: 5px;
}

.navigation-item.active {
  background-color: grey;
  color: white;
  font-weight: bold;
}


#sections {
  height: 100vh;
  overflow: auto;
}

.section-page {
  margin: 20px;
  height: 100vh;
  box-sizing: border-box;
  padding: 0 20px 20px;
}

.section-page:nth-child(1) {
  background-color: crimson;
}

.section-page:nth-child(2) {
  background-color: darkgreen;
}

.section-page:nth-child(3) {
  background-color: darkorange;
}

.section-page:nth-child(4) {
  background-color: darkblue;
}

.content {
  padding-top: 20px;
  color: white;
  position: sticky;
  top: 0;
}
<main>
  <nav>
    <ul id="nav">
      <li>
        <a href="#nav-home" class="navigation-item active">HOME</a>
      </li>
      <li>
        <a href="#nav-gallery" class="navigation-item">GALLERY</a>
      </li>
      <li>
        <a href="#nav-info" class="navigation-item">INFO</a>
      </li>
      <li>
        <a href="#nav-contact" class="navigation-item">CONTACT</a>
      </li>
      
    </ul>
  </nav>
  <div id="sections">
    <section class="section-page" id="nav-home">
      <div class="content">Home section</div>
    </section>
    <section class="section-page" id="nav-gallery">
      <div class="content">Gallery section</div>
    </section>
    <section class="section-page" id="nav-info">
      <div class="content">Info section</div>
    </section>
    <section class="section-page" id="nav-contact">
      <div class="content">Contact section</div>
    </section>
  </div>
</main>


推荐阅读