javascript - 检查元素在视口中是否可见
问题描述
我现在已经尝试了多种方法来做到这一点,我已经完成了 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>
解决方案
这是一个使用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>
推荐阅读
- python - 替换 BeautifulSoup 列表中的元素文本
- linux - 如何在启动时运行 GStreamer(Aravissrc 和 AWS Kinesis Video Stream)
- haskell - 加密函数错误,可能在 then 语句中,修复代码
- spring - Spring Boot JPA 返回列表列表而不是对象列表
- kotlin - 为什么是LiveData的getValue
可以为空? - laravel - 安装了 Laravel-mix 但公共文件夹中的 css 和 js 文件没有内容
- react-native - 在 react native 中选择图片
- c++ - 如何在 C++ 中查找一个范围内出现了多少个数字?
- javascript - 如何使用 javascript 创建导航菜单 li?以及如何在单击菜单选项后平滑滚动到部分?
- mongodb - MongoDB-shell 默认配置