检查元素是否在视口中可见

问题描述 投票:0回答:1

我已经尝试了多种方法来执行此操作,虽然我现在达到了80%,但仍然还不够。我有不同大小的部分。在我的导航中,当我在相应部分上时,我尝试在其中添加活动类,表明它是活动的。

我的JS

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');
            }
        })
    })
})   

我的HTML

<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>

[它趋向于越野车,在导航画廊内,我有一个砌体画廊,而且滑滑条我不确定这是否会影响它。但是活动的班级倾向于在导航中停留在图库上。每个部分的高度至少为100vh。

根据Cedric的建议,我尝试使用intersection observer 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);
  });
javascript jquery viewport
1个回答
0
投票

除了将GALLERY的类拼写错误外,您的代码一切正常。一旦修复,就不再具有粘性。

[通常,当您愿意像以前一样向我们提供代码时(感谢您,这非常有帮助),如果您可以制作StackOverflows特殊的HTML + JS + CSS片段之一,那就太好了。它以与您相同的方式显示所有代码,但也允许运行该代码并立即将其复制到答案中,以便对其进行一些编辑。我已经使用了这样的代码片段作为回答:

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 () {
            if(elementIsInView($(this)[0])){
                // 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;
}

.active {
  color: red;
}

.navigation-fixed {
  position: fixed;
}
<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="navigation-item">GALLERY</a>
    </li>
    <li>
      <a href="#nav-home" class="navigation-item active">Home</a>
    </li>
  </ul>
</nav>

<section class="section-page">Spacing</section>

<section class="section-page" id="nav-gallery">gallery here</section>
<section class="section-page">Spacing</section>

<section class="section-page" id="nav-info">info here</section>

<section class="section-page">Spacing</section>

<section class="section-page" id="nav-contact">contact here</section>
<section class="section-page">Spacing</section>

0
投票

这里是一个使用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>
© www.soinside.com 2019 - 2024. All rights reserved.