Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sticky element with scroll-based top updates causes flickering or shaking on scroll

I’m building a scroll effect where .logo and .content elements inside .container blocks use position: sticky and also receive scroll-based top position adjustments using jQuery.

The goal is to keep these elements visually synced while scrolling through multiple full-height sections.

A smooth sticky scroll where .logo and .content stay perfectly in sync without visual shaking or micro-jumps.

Any advice or better way to handle this?

// Auto-jquery-cache function
$C = (($) => {
  let c = {};
  return (s, f) => {
    if (!c[s] || f) {
      c[s] = $(s);
    }
    return c[s];
  };
})($);
$(document).ready(function() {
  const $diamond = $C('.diamond').find('.logo');
  const $ruby = $C('.ruby').find('.logo');
  const $emerald = $C('.emerald').find('.logo');

  // Add the same cache for the content divs
  const $diamondContent = $C('.diamond').find('.content');
  const $rubyContent = $C('.ruby').find('.content');
  const $emeraldContent = $C('.emerald').find('.content');

  const diamondTop = $diamond.offset().top;
  const diaRelTop = $diamond.position().top;
  const rubyTop = $ruby.offset().top;
  const emeraldTop = $emerald.offset().top;

  // Initial position adjustment
  $ruby.css('top', -rubyTop + diamondTop + diaRelTop);
  $emerald.css('top', -emeraldTop + diamondTop + diaRelTop);

  // Synchronize position for content elements
  $rubyContent.css('top', -rubyTop + diamondTop + diaRelTop);
  $emeraldContent.css('top', -emeraldTop + diamondTop + diaRelTop);

  const handle = (e) => {
    e.preventDefault();
    const t = $(window).scrollTop();

    // Update the position for both .logo and .content
    $diamond.css('top', t + diamondTop);
    $ruby.css('top', -rubyTop + $diamond.offset().top + diaRelTop);
    $emerald.css('top', -emeraldTop + $diamond.offset().top + diaRelTop);

    $diamondContent.css('top', t + diamondTop);
    $rubyContent.css('top', -rubyTop + $diamond.offset().top + diaRelTop);
    $emeraldContent.css('top', -emeraldTop + $diamond.offset().top + diaRelTop);
  };

  $(window).on('scroll', handle);
});
*,
*:before,
*:after {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

html {
  overflow-x: hidden;
}

body {
  -webkit-text-size-adjust: none;
  background: #fff;
}

.fixed {
  position: fixed;
  width: 100%;
  text-align: center;
  z-index: 10;
  background: white;
}

.container {
  height: 100dvh;
  overflow: hidden;
  position: relative;
}

.container .image-container img {
  width: 100%;
  height: auto;
  object-fit: cover;
}

.container .logo {
  position: relative;
  /* top: 0%;
            left: calc(50% - 440px); */
  width: 500px;
  height: 400px;
  object-fit: cover;
}

.content {
  display: flex;
  flex-direction: column;
  gap: 12px;
  position: relative;

  /* top: 0%;
            left: 40%; */
}

@media screen and (max-width: 480px) {
  .container .logo {
    width: 250px;
    margin-left: -125px;
  }
}

.container.diamond {
  background: #F0F8FF;
}

.container.ruby {
  background: #DC143C;
}

.container.emerald {
  background: #00FF7F;
}

.container .logo,
.container .content {
  will-change: transform;
  transition: transform 0.3s linear;
}

.sticky {
  position: sticky;
  top: 50px;
  left: 50px;
  z-index: 1;
}
<div id="wrapper" class="scroll">
  <div class="container diamond">
    <div class="sticky">
      <a href="https://www.google.com/">
                    <div class="logo"><img
                            src="https://images.unsplash.com/photo-1747134392051-5e112c58ce1e?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw0fHx8ZW58MHx8fHx8"
                            alt="Image 1" class="image"></div>
                </a>
      <a href="https://www.google.com/">
                    <div class="content">
                        <h3>MAKERSPLACE 1</h3>
                        <span>2022-2025</span>
                        <span>[View project]</span>
                    </div>
                </a>
    </div>
  </div>
  <div class="container ruby">
    <div class="sticky">
      <a href="https://www.youtube.com/">
                    <div class="logo"><img
                            src="https://images.unsplash.com/photo-1750101272034-7becde7454dd?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw4fHx8ZW58MHx8fHx8"
                            alt="Image 1" class="image"></div>
                </a>
      <a href="https://www.google.com/">
                    <div class="content">
                        <h3>MAKERSPLACE 2</h3>
                        <span>2022-2025</span>
                        <span>[View project]</span>
                    </div>
                </a>
    </div>
  </div>
  <div class="container emerald">
    <div class="sticky">
      <a href="https://www.linkedin.com/help/linkedin">
                    <div class="logo"><img
                            src="https://images.unsplash.com/photo-1749984340771-c3a967db0a28?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHwyMHx8fGVufDB8fHx8fA%3D%3D"
                            alt="Image 1" class="image"></div>
                </a>
      <a href="https://www.google.com/">
                    <div class="content">
                        <h3>MAKERSPLACE 3</h3>
                        <span>2022-2025</span>
                        <span>[View project]</span>
                    </div>
                </a>
    </div>
  </div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

code - https://codepen.io/Rejuanul-Islam/pen/WbvLJwQ

like image 368
S.M.Rejuanul Islam Avatar asked Sep 15 '25 12:09

S.M.Rejuanul Islam


1 Answers

Instead of using JavaScript to calculate where items should be moved using computation intensive scroll event, what would I do is another approach that involves CSS clip-path

  • create a wrapper .full of 100dvh height,
  • set each .container to the same height (100%)
  • place position: fixed panes (.fixed) inside each container
  • use CSS clip-path on the container

* { margin: 0; box-sizing: border-box; }

.full {
  height: 100dvh;
}

.container {
  clip-path: polygon(0 0, 100% 0%, 100% 100%, 0% 100%);
  height: 100%;
  background-color: var(--bg);
  color: #fff;
}

.fixed {
  position: fixed;
  inset: 0;
}


/* Other styles */

.fixed {
  display: grid;
  padding: min(2rem, 5vmin); /* Some space around to see the nice bg */
  & > * {
    grid-area: 1 / 1;  /* overlap children on top of each other */
  }
}

.logo {
  position: relative;

  .image {
    position: absolute;
    width: 100%; height: 100%;
    object-fit: cover;
  }
}

.content {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  align-self: end;
  padding: 2rem;
}
<div class="full">
  <div class="container" style="--bg:#BDE0FF;">
    <div class="fixed">
      <div class="logo">
        <img
                 src="https://images.unsplash.com/photo-1747134392051-5e112c58ce1e?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw0fHx8ZW58MHx8fHx8"
                 alt="Image 1"
                 class="image">
      </div>
      <div class="content">
        <h3>1 LOREM</h3>
        <span>2022-2025</span>
        <span>[View project]</span>
      </div>
    </div>
  </div>

  <div class="container" style="--bg:#DC143C;">
    <div class="fixed">
      <div class="logo">
        <img
                 src="https://images.unsplash.com/photo-1750101272034-7becde7454dd?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw4fHx8ZW58MHx8fHx8"
                 alt="Image 1"
                 class="image">
      </div>
      <div class="content">
        <h3>2 IPSUM</h3>
        <span>2022-2025</span>
        <span>[View project]</span>
      </div>
    </div>
  </div>

  <div class="container" style="--bg:#00FF7F;">
    <div class="fixed">
      <div class="logo">
        <img
                 src="https://images.unsplash.com/photo-1749984340771-c3a967db0a28?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHwyMHx8fGVufDB8fHx8fA%3D%3D"
                 alt="Image 1"
                 class="image">
      </div>
      <div class="content">
        <h3>3 DOLOR</h3>
        <span>2023-2023</span>
        <span>[View project]</span>
      </div>
    </div>
  </div>
</div>

To keep the HTML markup as simple and readable, I've removed the links, etc. You can easily put them back what is necessary.

Here's an example with even less styles and HTML markup:

* { margin: 0; box-sizing: border-box; }

.full {
  height: 100dvh;
}

.container {
  clip-path: polygon(0 0, 100% 0%, 100% 100%, 0% 100%);
  height: 100%;
  background-color: var(--bg);
  color: #fff;
}

.fixed {
  position: fixed;
  inset: 0;
}


/* Other styles */

.fixed {
  display: grid;
  padding: min(2rem, 5vmin); /* Some space around to see the nice bg */
  &>* {
    grid-area: 1 / 1; /* overlap children on top of each other */
  }
}

.image {
  width: 100%;
  min-height: 100%;
  height: 100%;
  object-fit: cover;
}

.content {
  align-self: end;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  padding: 2rem;
}
<div class="full">
  <div class="container" style="--bg:#BDE0FF;">
    <div class="fixed">
      <img
               src="https://images.unsplash.com/photo-1747134392051-5e112c58ce1e?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw0fHx8ZW58MHx8fHx8"
               alt="Image 1"
               class="image">
      <div class="content">
        <h3>1 LOREM</h3>
        <span>2022-2025</span>
        <span>[View project]</span>
      </div>
    </div>
  </div>

  <div class="container" style="--bg:#DC143C;">
    <div class="fixed">
      <img
               src="https://images.unsplash.com/photo-1750101272034-7becde7454dd?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw4fHx8ZW58MHx8fHx8"
               alt="Image 1"
               class="image">
      <div class="content">
        <h3>2 IPSUM</h3>
        <span>2022-2025</span>
        <span>[View project]</span>
      </div>
    </div>
  </div>

  <div class="container" style="--bg:#00FF7F;">
    <div class="fixed">
      <img
               src="https://images.unsplash.com/photo-1749984340771-c3a967db0a28?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHwyMHx8fGVufDB8fHx8fA%3D%3D"
               alt="Image 1"
               class="image">
      <div class="content">
        <h3>3 DOLOR</h3>
        <span>2023-2024</span>
        <span>[View project]</span>
      </div>
    </div>
  </div>
</div>
like image 144
Roko C. Buljan Avatar answered Sep 17 '25 02:09

Roko C. Buljan