首页 > 解决方案 > 检测元素何时跨越某个类的任何 div

问题描述

我有一个深色菜单,有时会跨越具有相同深色背景的部分,因此我尝试切换其类以在每次跨越深色部分时更改其颜色。

$(window).scroll(function(){
 var fixed = $("section.fixed-header");

 var fixed_position = $("section.fixed-header").offset().top;
 var fixed_height = $("section.fixed-header").height();

 var toCross_position = $(".dark").offset().top;
 var toCross_height = $(".dark").height();

 if (fixed_position + fixed_height  < toCross_position) {
   fixed.removeClass('light-menu');
 } else if (fixed_position > toCross_position + toCross_height) {
   fixed.removeClass('light-menu');
 } else {
   fixed.addClass('light-menu');
 }

});

当我在同一页面内只有一个带有深色类的 div 时,这很好用。但是,如果在同一页面内有多个不同的 div 具有暗类,则它仅适用于第一个 div。我怎么能在这里包含所有其他具有相同黑暗类的 div?

标签: javascriptjquery

解决方案


您应该看看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>


推荐阅读