检查元素在视口中是否可见
Check if element is visible in viewport
我现在已经尝试了多种方法来做到这一点,我已经完成了 80%,但还不够。
我有不同大小的部分。当我在相应的部分时,我试图在导航中添加活动 class,显示它处于活动状态。
代码容易出现错误,在导航库中,我有一个砖石库和光滑的滑块我不确定这是否会影响它。但是活跃的 class 倾向于停留在画廊的导航中。
每个部分的高度至少为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);
});
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>
除了 GALLERY 的 class 拼写错误外,您的代码看起来一切正常。一旦修复,它就不再粘了。
一般来说,如果您愿意向我们提供您所拥有的代码(顺便说一句,谢谢,它非常有帮助),如果您能使 Whosebugs 中的一个变得特别,那就太棒了 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>
这是一个使用 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>
我现在已经尝试了多种方法来做到这一点,我已经完成了 80%,但还不够。 我有不同大小的部分。当我在相应的部分时,我试图在导航中添加活动 class,显示它处于活动状态。
代码容易出现错误,在导航库中,我有一个砖石库和光滑的滑块我不确定这是否会影响它。但是活跃的 class 倾向于停留在画廊的导航中。 每个部分的高度至少为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);
});
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>
除了 GALLERY 的 class 拼写错误外,您的代码看起来一切正常。一旦修复,它就不再粘了。
一般来说,如果您愿意向我们提供您所拥有的代码(顺便说一句,谢谢,它非常有帮助),如果您能使 Whosebugs 中的一个变得特别,那就太棒了 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>
这是一个使用 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>