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
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
.full of 100dvh height,.container to the same height (100%)position: fixed panes (.fixed) inside each containerclip-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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With