2 回答
TA贡献1876条经验 获得超6个赞
您应该查看Intersection Observer (IO),而不是监听滚动事件。这旨在解决像您这样的问题。它比听滚动事件然后自己计算位置要高效得多。
当然你可以继续只使用滚动事件,W3C的官方 Polyfill使用滚动事件来模拟旧浏览器的 IO。侦听滚动事件和计算位置的性能不高,尤其是在有多个元素的情况下。因此,如果您关心用户体验,我真的建议您使用 IO。只是想添加此答案以显示此类问题的现代解决方案是什么。
我花时间创建了一个基于 IO 的示例,这应该可以帮助您入门。
基本上我定义了两个阈值:一个是 20%,一个是 90%。如果元素在视口中占 90%,则可以假设它将覆盖标题。因此,我将标题的类设置为视图中 90% 的元素。
第二个阈值是 20%,这里我们要检查元素是从顶部还是从底部进入视图。如果它从顶部可见 20%,那么它将与标题重叠。
如您所见,调整这些值并调整逻辑。
编辑:根据您的评论对其进行编辑,请注意,如果您从我的代码中删除 console.log,您可能会看到更好的效果,这样它们就不会干扰您的视图。
我添加了一个标题不变的 div(绿色的)
const sections = document.querySelectorAll('.menu');
const config = {
rootMargin: '0px',
threshold: [.2, .9]
};
const observer = new IntersectionObserver(function (entries, self) {
entries.forEach(entry => {
console.log(entry); // log the IO entry for demo purposes
console.log(entry.target); // here you have the Element itself.
// you can test for className here for example
if (entry.isIntersecting) {
var headerEl = document.querySelector('header');
if (entry.intersectionRatio > 0.9) {
//intersection ratio bigger than 90%
//-> set header according to target
headerEl.className=entry.target.dataset.header;
} else {
//-> check if element is coming from top or from bottom into view
if (entry.target.getBoundingClientRect().top < 0 ) {
headerEl.className=entry.target.dataset.header;
}
}
}
});
}, config);
sections.forEach(section => {
observer.observe(section);
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.g-100vh {
height: 100vh
}
header {
min-height: 50px;
position: fixed;
background-color: green;
width: 100%;
}
header.white-menu {
color: white;
background-color: black;
}
header.black-menu {
color: black;
background-color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<header>
<p>Header Content </p>
</header>
<div class="grid-30-span g-100vh menu" style="background-color:darkblue;" data-header="white-menu">
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1.414 1'%3E%3C/svg%3E"
data-src="/images/example_darkblue.jpg"
class="lazyload"
alt="<?php echo $title; ?>">
</div>
<div class="grid-30-span g-100vh no-menu" style="background-color:green;" data-header="black-menu">
<h1> Here no change happens</h1>
<p>it stays at whatever state it was before, depending on if you scrolled up or down </p>
</div>
<div class="grid-30-span g-100vh menu" style="background-color:lightgrey;" data-header="black-menu">
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1.414 1'%3E%3C/svg%3E"
data-src="/images/example_lightgrey.jpg"
class="lazyload"
alt="<?php echo $title; ?>">
</div>
TA贡献1809条经验 获得超8个赞
原因是,然而 JQuery 选择器选择所有带有.dark类的元素,当你在其上链接.offset().topor.height()方法时,它只会将第一个保存到变量中:
var toCross_position = $(".dark").offset().top;
var toCross_height = $(".dark").height();
您可以将所有位置和高度映射到数组中,然后您还应该使
var toCross_position = $(".dark").offset().top;
var toCross_height = $(".dark").height();
// only the first div's pos and height:
console.log(toCross_height, toCross_position);
var positions = $('.dark').toArray().map(elem => $(elem).offset().top);
var heights = $('.dark').toArray().map(elem => $(elem).height());
console.log('all .dark positions:', positions);
console.log('all .dark heights:', heights);
.dark {
background-color: #222;
color: white;
margin-bottom: 2rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="dark">Dark</div>
<div class="dark">Dark</div>
<div class="dark">Dark</div>
<div class="dark">Dark</div>
通过循环这些值来检查您的标题是否穿过它们。
添加回答
举报