Adding CSS class using offsetHeight (Javascript) causes elements to flicker

问题描述 投票:0回答:6

我已经重新创建了我的问题的工作简化版本:一个瘦横幅(当用户向下滚动时隐藏)、主导航(当用户向下滚动时应该坚持到页面顶部)和一些虚拟内容以使滚动成为可能。

我通过在滚动位置小于或等于

sb-scrolling
高度时向瘦横幅(
display: none
)添加类
main-nav
来实现当前的实现。

但是,当缓慢滚动时,有一个闪烁,似乎在隐藏和显示瘦横幅之间。谁能指导我哪里出错了?

更新:下面的答案仍然存在“闪烁”(必须缓慢向下/向上滚动才能发现)

      const skinnyBanner = document.querySelector('.skinny-banner');
      const mainNav = document.querySelector('.main-nav');

      // Handle page scroll
      let scrollpos = window.scrollY;
      const navHeight = mainNav.offsetHeight;

      window.addEventListener('scroll', function() {
        scrollpos = window.scrollY;

        if (scrollpos >= navHeight) {
          mainNav.classList.add('scrolling');
          skinnyBanner.classList.add('sb-scrolling');
        } else {
          mainNav.classList.remove('scrolling');
          skinnyBanner.classList.remove('sb-scrolling');
        }
      });
header {
  display: block;
  position: sticky;
  top: 0;
}

.skinny-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: lightgrey;
  width: 100%;
  height: 40px;
}

.main-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: yellow;
  width: 100%;
  height: 140px;
}

.skinny-nav-menu {
  display: flex;
  gap: 24px;
}

.sb-scrolling {
  display: none !important;
}

.scrolling {
  min-height: 70px !important;
}

.content-block-1 {
  height: 300px;
  background-color: orange;
  display: flex;
  align-items: center;
  justify-content: center;
}

.content-block-2 {
  height: 300px;
  background-color: lightblue;
  display: flex;
  align-items: center;
  justify-content: center;
}

.content-block-3 {
  height: 300px;
  background-color: lightcyan;
  display: flex;
  align-items: center;
  justify-content: center;
}
<header>
  <nav>
    <div class="skinny-banner">SKINNY BANNER THAT IS HIDDEN WHEN SCROLLING DOWN</div>
    <div class="main-nav">
    MAIN NAVIGATION THAT SHOULD ALWAYS BE VISIBLE
    </div>
  </nav>
</header>

<div class="content-block-1">RANDOM PAGE CONTENT</div>
<div class="content-block-2">RANDOM PAGE CONTENT</div>
<div class="content-block-3">RANDOM PAGE CONTENT</div>

javascript html css sticky
6个回答
2
投票

编辑

这是一种新的、不同的方式,它使用

position: fixed
作为标题,这样它就不会影响文档的高度,当我们向下滚动时,我们会将顶部横幅从屏幕顶部滑出。文档的主要内容将有一个 140px
margin-top
,这样它就不会隐藏在标题后面。

      const header = document.getElementById("header");

      // Handle page scroll
      const navHeight = document.getElementsByClassName("main-nav")[0].offsetHeight;

      window.addEventListener('scroll', () => {
        const scrollpos = window.scrollY;

        if (scrollpos >= navHeight) {
          header.classList.add("moveUp");
        } else {
          header.classList.remove("moveUp");
        }
      });
body{
  --mainNavHeight: 140px;
  margin: 0px;
}

main{
  margin-top: var(--mainNavHeight);
}

header {
  display: block;
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  transition: transform 0.4s;
}

.moveUp{
  transform: translateY(-40px);
}

.centerContent{
  display: flex;
  align-items: center;
  justify-content: center;
}

.skinny-banner {

  background-color: lightgrey;
  width: 100%;
  height: 40px;
  top: 0;
}

.main-nav {
  background-color: yellow;
  width: 100%;
  height: var(--mainNavHeight);
}

.content-block {
  height: 300px;
}
<header id="header">
  <nav>
    <div class="skinny-banner centerContent">SKINNY BANNER THAT IS HIDDEN WHEN SCROLLING DOWN</div>
    <div class="main-nav centerContent">
    MAIN NAVIGATION THAT SHOULD ALWAYS BE VISIBLE
    </div>
  </nav>
</header>

<main>
  <div class="content-block centerContent" style="background-color: orange">RANDOM PAGE CONTENT</div>
  <div class="content-block centerContent" style="background-color: lightblue">RANDOM PAGE CONTENT</div>
  <div class="content-block centerContent" style="background-color: lightcyan">RANDOM PAGE CONTENT</div>
</main>

结束编辑

这样的事情怎么样?我用

position: fixed
将瘦横幅粘贴到标题的顶部,并给
main-nav
一些额外的顶部填充,以便它保持在同一个位置。一旦我们向下滚动并使用 CSS
transition
,我们就可以删除额外的填充,所以它看起来不错。我做的另一个改变是将你的 body margin 设置为 0,只是为了让事情变得更容易。

      const skinnyBanner = document.querySelector('.skinny-banner');
      const mainNav = document.querySelector('.main-nav');

      // Handle page scroll
      let scrollpos = window.scrollY;
      const navHeight = mainNav.offsetHeight;

      window.addEventListener('scroll', function() {
        scrollpos = window.scrollY;

        if (scrollpos >= navHeight) {
          mainNav.classList.add('scrolling');
          skinnyBanner.classList.add('sb-scrolling');
        } else {
          mainNav.classList.remove('scrolling');
          skinnyBanner.classList.remove('sb-scrolling');
        }
      });
body{
  margin: 0px;
}

header {
  display: block;
  position: sticky;
  top: 0;
}

.skinny-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: lightgrey;
  width: 100%;
  height: 40px;
  position: fixed;
  top: 0;

}

.main-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: yellow;
  width: 100%;
  height: 140px;
  padding-top: 40px;
  transition: padding .5s;
}

.skinny-nav-menu {
  display: flex;
  gap: 24px;
}

.sb-scrolling {
  display: none;
}

.scrolling {
  min-height: 70px !important;
  padding-top: 0px;
}

.content-block-1 {
  height: 300px;
  background-color: orange;
  display: flex;
  align-items: center;
  justify-content: center;
}

.content-block-2 {
  height: 300px;
  background-color: lightblue;
  display: flex;
  align-items: center;
  justify-content: center;
}

.content-block-3 {
  height: 300px;
  background-color: lightcyan;
  display: flex;
  align-items: center;
  justify-content: center;
}
<header>
  <nav>
    <div class="skinny-banner">SKINNY BANNER THAT IS HIDDEN WHEN SCROLLING DOWN</div>
    <div class="main-nav">
    MAIN NAVIGATION THAT SHOULD ALWAYS BE VISIBLE
    </div>
  </nav>
</header>

<div class="content-block-1">RANDOM PAGE CONTENT</div>
<div class="content-block-2">RANDOM PAGE CONTENT</div>
<div class="content-block-3">RANDOM PAGE CONTENT</div>


0
投票

问题是因为

.sb-scrolling {
  display: none !important;
}

它只是让

div
在它添加到那个div的那一刻消失,所以你看到闪烁效果。

const skinnyBanner = document.querySelector('.skinny-banner');
const mainNav = document.querySelector('.main-nav');

// Handle page scroll
let scrollpos = window.scrollY;
const navHeight = mainNav.offsetHeight;

window.addEventListener('scroll', function() {
  scrollpos = window.scrollY;

  if (scrollpos >= navHeight) {
    mainNav.classList.add('scrolling');
    skinnyBanner.classList.add('sb-scrolling');
  } else {
    mainNav.classList.remove('scrolling');
    skinnyBanner.classList.remove('sb-scrolling');
  }
});
header {
  display: block;
  position: sticky;
  top: 0;
}

.skinny-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: lightgrey;
  width: 100%;
  height: 40px;
}

.main-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: yellow;
  width: 100%;
  height: 140px;
}

.skinny-nav-menu {
  display: flex;
  gap: 24px;
}

.sb-scrolling {
  height: 0 !important;
  padding: 0;
  transition: all 0.2s linear;
  overflow:hidden;
}

.scrolling {
  min-height: 70px !important;
}

.content-block-1 {
  height: 300px;
  background-color: orange;
  display: flex;
  align-items: center;
  justify-content: center;
}

.content-block-2 {
  height: 300px;
  background-color: lightblue;
  display: flex;
  align-items: center;
  justify-content: center;
}

.content-block-3 {
  height: 300px;
  background-color: lightcyan;
  display: flex;
  align-items: center;
  justify-content: center;
}
<header>
  <nav>
    <div class="skinny-banner">SKINNY BANNER THAT IS HIDDEN WHEN SCROLLING DOWN</div>
    <div class="main-nav">
      MAIN NAVIGATION THAT SHOULD ALWAYS BE VISIBLE
    </div>
  </nav>
</header>

<div class="content-block-1">RANDOM PAGE CONTENT</div>
<div class="content-block-2">RANDOM PAGE CONTENT</div>
<div class="content-block-3">RANDOM PAGE CONTENT</div>


0
投票

当您将横幅的显示属性设置为 none 时,它会从 DOM 流中消失,因此会更改 scrollpos 值(将其降低到您的阈值 navHeight 以下)。这使得横幅再次出现并重新开始。

如果您只是想在向下滚动时隐藏横幅并且不希望它重新出现,只需将 navHeight 设置为 -1 一次 scrollpos > navHeight。


0
投票

您可以进一步完善它,但主要思想是让整个标题为

position: fixed;
然后当您基于阈值(比如 25px)滚动时,您切换标题上的“
show
”类。

这样你就可以直接在 CSS 中控制所有的过渡和动画。

为了给标题留出空间,您可以固定它的高度(它会在不同的断点处改变),或者您可以在切换类时更新它。

let headerHeight = '85px';
let showingSkinny = true;

const updateCSSHeightVar = (height) => {
  document.body.style.setProperty('--header-height', height + 'px');
};

const showSkinny = (show, header) => {
  if (show === showingSkinny) return;
  
  showingSkinny = show;
  if (show) {
    header.classList.add('show');
  } else {
    header.classList.remove('show');
  }
  
  headerHeight = header.clientHeight;
  updateCSSHeightVar(headerHeight);
};

  
onload = (event) => {
  console.log('loaded');
  const header = document.querySelector('#header');
  headerHeight = header.clientHeight;
  updateCSSHeightVar(headerHeight);
  
  addEventListener("scroll", (event) => {
    if (window.scrollY > 25) showSkinny(true, header)
    else showSkinny(false, header);
  });
};
header {
  position: fixed;
  top: 0;
  z-index: 1;
  background: white;
  width: 100%; 
  transition: 500ms;
}

header.show .skinny {
  max-height: 0;
  opacity: 0;
}
.skinny {
  transition: 300ms;
}
.main-nav {
  background: yellow;
}

main {
  margin-top: var(--header-height);
}

.block {
  height: 500px;
  background: blue;
}

.block:nth-child(even) {
  background: red;
}
<body>
  <header id="header">
    <div class="skinny"><p>Skinny</p></div>
    <div class="main-nav"><p>Main nav</p></div>
  </header>
  <main>
    <div class="block"><p>Hello World!</p></div>
    <div class="block"><p>Hello World!</p></div>
    <div class="block"><p>Hello World!</p></div>
    <div class="block"><p>Hello World!</p></div>
    <div class="block"><p>Hello World!</p></div>
  </main>
</body>


0
投票

“闪烁”问题的存在是因为当您将“显示:无”应用于瘦横幅时,它会触发页面上的滚动事件并且窗口滚动量低于导航高度值,然后再次触发滚动事件并删除“显示:无”再次向页面添加瘦横幅和更高的高度,从而导致另一个滚动。基本上,在滚动时的某个时刻,它陷入了添加和删除显示标签的无限循环,这使得它“闪烁”。

解决这个问题的技巧是考虑瘦横幅的高度,在附加代码中检查第二个 else 并在比较窗口滚动量之前从 navheight 中减去瘦元素高度,它将被修复。

const skinnyBanner = document.querySelector('.skinny-banner');
const mainNav = document.querySelector('.main-nav');

// Handle page scroll
let scrollpos = window.scrollY;
const navHeight = mainNav.offsetHeight;
let bannerHeight = 0;
let skipScroll = false;

window.addEventListener('scroll', function() {
  scrollpos = window.scrollY;
  
  if (scrollpos >= navHeight && !skipScroll) {
    bannerHeight = skinnyBanner.offsetHeight;
    mainNav.classList.add('scrolling');
    skinnyBanner.classList.add('sb-scrolling');
    skipScroll = true;
  } else if (scrollpos < navHeight - bannerHeight) {
    bannerHeight = 0;
    if(skipScroll){
      skipScroll = false;
    }
    else{
      mainNav.classList.remove('scrolling');
      skinnyBanner.classList.remove('sb-scrolling');
    }
  }
});

0
投票

唯一缺少的是

transition: all 0.2s linear;
中的
.skinny-banner
。其余来自@AlanOmar

const skinnyBanner = document.querySelector('.skinny-banner');
const mainNav = document.querySelector('.main-nav');

// Handle page scroll
let scrollpos = window.scrollY;
const navHeight = mainNav.offsetHeight;

window.addEventListener('scroll', function() {
  scrollpos = window.scrollY;

  if (scrollpos >= navHeight) {
    skinnyBanner.classList.add('sb-scrolling');
  } else {
    skinnyBanner.classList.remove('sb-scrolling');
  }
});
header {
  display: block;
  position: sticky;
  top: 0;
}

.skinny-banner {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: lightgrey;
  width: 100%;
  height: 40px;
  transition: all 0.2s linear;
}

.main-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: yellow;
  width: 100%;
  height: 140px;
}

.skinny-nav-menu {
  display: flex;
  gap: 24px;
}

.sb-scrolling {
  height: 0 !important;
  padding: 0;
  transition: all 0.2s linear;
  overflow:hidden;
}

.content-block-1 {
  height: 300px;
  background-color: orange;
  display: flex;
  align-items: center;
  justify-content: center;
}

.content-block-2 {
  height: 300px;
  background-color: lightblue;
  display: flex;
  align-items: center;
  justify-content: center;
}

.content-block-3 {
  height: 300px;
  background-color: lightcyan;
  display: flex;
  align-items: center;
  justify-content: center;
}
<header>
  <nav>
    <div class="skinny-banner">SKINNY BANNER THAT IS HIDDEN WHEN SCROLLING DOWN</div>
    <div class="main-nav">
      MAIN NAVIGATION THAT SHOULD ALWAYS BE VISIBLE
    </div>
  </nav>
</header>

<div class="content-block-1">RANDOM PAGE CONTENT</div>
<div class="content-block-2">RANDOM PAGE CONTENT</div>
<div class="content-block-3">RANDOM PAGE CONTENT</div>

更新

transition: height 0.2s linear;
应该足够了。

© www.soinside.com 2019 - 2024. All rights reserved.