使用“路口观察器”(IO)更改样式标题/导航

KP8*_*P83 7 html javascript css intersection-observer

小提琴最新


我从scroll event方法开始这个问题,但是由于建议使用IntersectionObserver似乎更好的方法,因此我试图以这种方式使其工作。


目标是什么:

我想改变风格(color+ background-color中的)header这取决于电流div/ section通过查找(我在想的?),观察其classdata将覆盖默认header样式(白色)。


标头样式:

font-color

根据内容(div/ section),默认值header应该只能将font-color变为两种可能的颜色:

  • 黑色
  • 白色

background-color

根据内容的不同,background-color颜色可能是无限的,也可能是透明的,因此最好将它们分开处理,这些可能是最常用的背景颜色:

  • 白色(默认)
  • 黑色
  • 无颜色(透明)

CSS:

header {
  position: fixed;
  width: 100%;
  top: 0;
  line-height: 32px;
  padding: 0 15px;
  z-index: 5;
  color: black; /* default */
  background-color: white; /* default */
}
Run Code Online (Sandbox Code Playgroud)

默认标题的Div / section示例,内容无变化:

<div class="grid-30-span g-100vh">
    <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_default_header.jpg" 
    class="lazyload"
    alt="">
</div>
Run Code Online (Sandbox Code Playgroud)

Div / section示例更改内容标题:

<div class="grid-30-span g-100vh" data-color="white" data-background="darkblue">
    <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="">
</div>

<div class="grid-30-span g-100vh" data-color="white" data-background="black">
    <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_black.jpg" 
    class="lazyload"
    alt="">
</div>
Run Code Online (Sandbox Code Playgroud)

交叉口观察员方法:

var mq = window.matchMedia( "(min-width: 568px)" );
if (mq.matches) {
  // Add for mobile reset

document.addEventListener("DOMContentLoaded", function(event) { 
  // Add document load callback for leaving script in head
  const header = document.querySelector('header');
  const sections = document.querySelectorAll('div');
  const config = {
    rootMargin: '0px',
    threshold: [0.00, 0.95]
  };

  const observer = new IntersectionObserver(function (entries, self) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        if (entry.intersectionRatio > 0.95) {
          header.style.color = entry.target.dataset.color !== undefined ? entry.target.dataset.color : "black";
          header.style.background = entry.target.dataset.background !== undefined ? entry.target.dataset.background : "white";   
        } else {
        if (entry.target.getBoundingClientRect().top < 0 ) {
          header.style.color = entry.target.dataset.color !== undefined ? entry.target.dataset.color : "black";
          header.style.background = entry.target.dataset.background !== undefined ? entry.target.dataset.background : "white";
          }
        } 
      }
    });
  }, config);

  sections.forEach(section => {
    observer.observe(section);
  });

});

}
Run Code Online (Sandbox Code Playgroud)

clo*_*ned 7

而不是听滚动事件,您应该看看Intersection Observer(IO)。旨在解决您的问题。它比聆听滚动事件然后自己计算位置要好得多。

首先,这是一个Codepen,它显示了您的问题的解决方案。我不是该Codepen的作者,我可能会做一些不同的事情,但是它肯定向您显示了解决问题的基本方法。

我将要更改的内容:在示例中,您可以看到,如果将99%的内容添加到新部分中,则标题的更改甚至变得很困难,新部分也不是完全可见的。

现在解决了这个问题,对它的工作原理进行了一些解释(请注意,我不会盲目地从codepen复制粘贴,我还将const更改为let,而是使用更适合您的项目的东西。

首先,您必须指定IO选项:

let options = {
  rootMargin: '-50px 0px -55%'
}

let observer = new IntersectionObserver(callback, options);
Run Code Online (Sandbox Code Playgroud)

在该示例中,一旦元素距离视图50px,IO将执行回调。我不能一味推荐一些更好的值,但如果有时间,我会尝试调整这些参数以查看是否可以获得更好的结果。

他们在codepen中定义了内联的回调函数,我只是用这种方式编写了它,以便更清楚地了解发生在什么地方。

IO的下一步是定义一些要监视的元素。在您的情况下,您应该在div中添加一些类,例如<div class="section">

let entries = document.querySelectorAll('div.section');
entries.forEach(entry => {observer.observe(entry);})
Run Code Online (Sandbox Code Playgroud)

最后,您必须定义回调函数:

entries.forEach(entry => {
    if (entry.isIntersecting) {
     //specify what should happen if an element is coming into view, like defined in the options. 
    }
  });
Run Code Online (Sandbox Code Playgroud)

编辑:正如我所说的,这只是一个如何入门的示例,它不是复制粘贴的最终解决方案。在基于可见部分的示例中,当前元素被突出显示。您必须更改此部分,以便不要将活动类设置为例如第三个元素,而是根据在Element上设置的某些属性来设置颜色和背景色。我建议为此使用数据属性

编辑2:当然,您可以继续使用滚动事件,W3C 的官方Polyfill使用滚动事件来模拟旧版浏览器的IO,只是侦听滚动事件和计算位置效果不佳,尤其是在有多个元素的情况下。因此,如果您关心用户体验,我真的建议您使用IO。只是想添加此答案,以显示针对该问题的现代解决方案。

编辑3:我花了一些时间来创建基于IO的示例,这应该使您入门。

基本上,我定义了两个阈值:一个为20%,一个为90%。如果元素在视口中占90%,则保存它会覆盖标题。因此,我将标题的类设置为视图中90%的元素。

第二个阈值是20%,这里我们必须检查元素是从顶部还是从底部进入视图。如果从顶部可见20%,则它将与标题重叠。

调整这些值并调整逻辑,如您所见。

let options = {
  rootMargin: '-50px 0px -55%'
}

let observer = new IntersectionObserver(callback, options);
Run Code Online (Sandbox Code Playgroud)
let entries = document.querySelectorAll('div.section');
entries.forEach(entry => {observer.observe(entry);})
Run Code Online (Sandbox Code Playgroud)
entries.forEach(entry => {
    if (entry.isIntersecting) {
     //specify what should happen if an element is coming into view, like defined in the options. 
    }
  });
Run Code Online (Sandbox Code Playgroud)


A. *_*shu 6

我可能不完全理解这个问题,但对于你的例子 - 你可以通过使用mix-blend-mode css 属性来解决它,而不需要使用 javascript。

例子:

header {background: white; position: relative; height: 20vh;}
header h1 {
  position: fixed;
  color: white;
  mix-blend-mode: difference;
}
div {height: 100vh; }
Run Code Online (Sandbox Code Playgroud)
<header>
  <h1>StudioX, Project Title, Category...</h1>
</header>
<div style="background-color:darkblue;"></div>
<div style="background-color:lightgrey;"></div>
Run Code Online (Sandbox Code Playgroud)