skip to Main Content

I’m working on a navbar that shrinks using intersection observer that adds a class accordingly but when the transition starts, it starts from 0 padding.

Here right when the .nav-scrolled is added, the CSS properties are also added to the nav bar. But right when it adds, the padding starts transitioning from 0. What can I do?

const nav = document.querySelector("nav");
const sectionOne = document.querySelector(".intersection");

const sectionOneObserver = new IntersectionObserver(function(entries, sectionOneObserver) {
  entries.forEach(entry => {
    if (!entry.isIntersecting) {
      nav.classList.add("nav-scrolled")
    } else {
      nav.classList.remove("nav-scrolled")
    }
  });
});

sectionOneObserver.observe(sectionOne)
.nav-container {
  display: flex;
  justify-content: center;
}

nav {
  position: fixed;
  width: 100vw;
  padding-block: 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  transition: 1s ease;
}

nav>* {
  margin-inline: 5%;
}

nav>div {
  width: 300px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  overflow: hidden;
  padding-block: 10px;
}

.nav-scrolled {
  width: 80%;
  padding-block: 15px;
  font-size: 20%;
  border-radius: 20px;
  transform: translateY(15px);
}

.nav-scrolled>* {
  margin-inline: 15px;
}
.intersection{height:10vh}

.body{color:red; height:120vh}
<div class="nav-container">
  <nav>
    <a href="#">
      <h1>Heading</h1>
      <h2>Heading-2</h2>
    </a>
    
    <div>
      <a href="#">link1</a>
      <a href="#">link2</a>
      <a href="#">link3</a>
    </div>
  </nav>
</div>

<div class="intersection"></div>
<div class="body"><div>

2

Answers


  1. It’s not that the padding starts from 0. This happens because you have margin-inline on the children of the nav element. This margin disappears when you scroll and the children don’t have a transition-tag, so that’s why it appears to be jumping. I just removed this css-line. Seems to work just fine 🙂

    const nav = document.querySelector("nav");
    const sectionOne = document.querySelector(".intersection");
    
    const sectionOneObserver = new IntersectionObserver(function(entries, sectionOneObserver) {
      entries.forEach(entry => {
        if (!entry.isIntersecting) {
          nav.classList.add("nav-scrolled")
        } else {
          nav.classList.remove("nav-scrolled")
        }
      });
    });
    
    sectionOneObserver.observe(sectionOne)
    .nav-container {
      display: flex;
      justify-content: center;
    }
    
    nav {
      position: fixed;
      width: 100vw;
      padding-block: 20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      transition: 1s ease;
    }
    
    nav>* {
      margin-inline: 5%;
    }
    
    nav>div {
      width: 300px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      overflow: hidden;
      padding-block: 10px;
    }
    
    .nav-scrolled {
      width: 80%;
      padding-block: 15px;
      font-size: 20%;
      border-radius: 20px;
      transform: translateY(15px);
    }
    
    .intersection{height:10vh}
    
    .body{color:red; height:120vh}
    <div class="nav-container">
      <nav>
        <a href="#">
          <h1>Heading</h1>
          <h2>Heading-2</h2>
        </a>
        
        <div>
          <a href="#">link1</a>
          <a href="#">link2</a>
          <a href="#">link3</a>
        </div>
      </nav>
    </div>
    
    <div class="intersection"></div>
    <div class="body"><div>
    Login or Signup to reply.
  2. It seems to be a problem with the .nav-scrolled>* selector. It’s the property change on this that is causing the jump. It’s easier to see this when the transition is extended to be 5s long.

    The problem is happening because you do not have a transition applied on nav>* even though you are changing properties when scrolled, thus an immediate jump with no transition visible. The fix is to move your transition to apply to both the parent .nav selector and the nav>* selector, like this:

    nav,
    nav>* {
      transition: 5s ease;
    }
    
    const nav = document.querySelector("nav");
    const sectionOne = document.querySelector(".intersection");
    
    const sectionOneObserver = new IntersectionObserver(function(entries, sectionOneObserver) {
      entries.forEach(entry => {
        if (!entry.isIntersecting) {
          nav.classList.add("nav-scrolled")
        } else {
          nav.classList.remove("nav-scrolled")
        }
      });
    });
    
    sectionOneObserver.observe(sectionOne)
    .nav-container {
      display: flex;
      justify-content: center;
    }
    
    nav {
      position: fixed;
      width: 100vw;
      padding-block: 20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    
    nav>* {
      margin-inline: 5%;
    }
    
    nav,
    nav>* {
      transition: 5s ease;
    }
    
    nav>div {
      width: 300px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      overflow: hidden;
      padding-block: 10px;
    }
    
    .nav-scrolled {
      width: 80%;
      padding-block: 15px;
      font-size: 20%;
      border-radius: 20px;
      transform: translateY(15px);
    }
    
    .nav-scrolled>* {
      margin-inline: 15px;
    }
    .intersection{height:10vh}
    
    .body{color:red; height:120vh}
    <div class="nav-container">
      <nav>
        <a href="#">
          <h1>Heading</h1>
          <h2>Heading-2</h2>
        </a>
        
        <div>
          <a href="#">link1</a>
          <a href="#">link2</a>
          <a href="#">link3</a>
        </div>
      </nav>
    </div>
    
    <div class="intersection"></div>
    <div class="body"><div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search