Steven Hale
Steven Hale

Reputation: 226

Adding position:sticky to element causes jumpy behaviour on scroll

Problem: I have a title container element that starts off as 100vh to act as a landing page. When the user scrolls it should reduce the title container element to 15vh and stick it to the top of the window. I've essentially done that but the transition isn't very smooth and jumps around when the user doesn't scroll heaps.

Intended outcome: When the user scrolls at all, it should trigger the title element to reduce in size to 15vh and stick to the top of the window with a smooth transition.

Things tried:

I have tried to give an example below with the minimum amount of code.

window.addEventListener(
  'scroll',
  function() {
    let scrollTop =
      window.pageYOffset ||
      (document.documentElement || document.body.parentNode || document.body)
      .scrollTop;
    getPosition();
    console.log(getPosition());
    if (getPosition() <= 0) {
      this.document.getElementById('title').classList.add('header');
    } else {
      this.document.getElementById('title').classList.remove('header');
    }
  },
  false
);

function getPosition() {
  element = document.getElementById('topTitleMarker');
  var clientRect = element.getBoundingClientRect();
  return clientRect.top;
}
.title {
  padding: 0;
  margin: 0;
  height: 100vh;
  font-weight: bold;
  font-family: 'Titillium Web', sans-serif;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  background-color: black;
  -webkit-transition: all 1s linear;
  -moz-transition: all 1s linear;
  -o-transition: all 1s linear;
  transition: all 1s linear;
  color: white;
}

.header {
  top: 0px;
  height: 15vh;
  position: sticky;
}

body {
  background-color: white;
  height: 500vh;
  -ms-overflow-style: none;
  scrollbar-width: none;
  border-bottom: 5px solid white;
  /*   overflow: hidden; */
}

.topTitleMarker {
  top: 1px;
  position: absolute;
}

.test {
  margin-top: 15vh;
}
<div id='title' class="title">TITLE</div>
<div id='topTitleMarker' class="topTitleMarker"></div>
<div class="test">TEST</div>

Codepen

Upvotes: 4

Views: 2334

Answers (1)

zer00ne
zer00ne

Reputation: 43880

Problems

I think the jerkiness in the scrolling was because of the .topTitleMarker was referenced as the top of scrolling element with .getBoundingClientRect().top and the transition was based on the height of .title when it increases at 100vh but there isn't a transition for when .title shrinks. Moreover, transition is a process heavy animation, but your animation is a simple one so no worries there.

Changes

JavaScript

  • Removed scrollTop -- it wasn't being used
  • Moved and consolidated getPosition() over to the event handler
  • Also changed the reference element for .getBoundingClientRect().top from .topTitleMarker to <h1> (in OP it's .title)

CSS

  • Removed transition from <h1>
  • Added animation: grow 1s forwards ease-out; to <h1>
  • Added animation: shrink 1s forwards ease-out; to .header
  • For smoother animation animation and @keyframes are used for shrinking and growing of <h1>

HTML

  • Removed everything from OP and added a <h1>

window.addEventListener('scroll', headerHeight);

function headerHeight(event) {
  const title = document.querySelector('h1');
  if (title.getBoundingClientRect().top <= 0) {
    title.classList.add('header');
  } else {
    title.classList.remove('header');
  }
};
@import url('https://fonts.googleapis.com/css2?family=Titillium+Web:wght@300;700&display=swap');

html {
  font: 300 2ch/1.25 'Titillium Web';
}

body {
  height: 500vh;
}

h1 {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100vh;
  margin: 0;
  padding: 0;
  font-weight: 700;
  color: white;
  background-color: black;
  animation: grow 1s forwards ease-out;
}

.header {
  position: sticky;
  top: 0px;
  height: 15vh;
  animation: shrink 1s forwards ease-out;
}

@keyframes grow {
  0% {
    height: 15vh
  }
  100% {
    height: 100vh
  }
}

@keyframes shrink {
  0% {
    height: 100vh
  }
  100% {
    height: 15vh
  }
}
<h1>TITLE</h1>

Upvotes: 3

Related Questions