I am developing a personal portfolio website with a top horizontal header and a horizontally scrollable content wrapper below it. Problem: when clicking on links the scrolling isn’t smooth. Also, the snap functionality doesn’t always work.
I want to achieve the following:
Smooth scrolling: When a user clicks on a navigation link in the header, the page should smoothly scroll to the corresponding section.
Snapping behavior: When the user manually scrolls horizontally, the page should snap to the nearest section, ensuring the sections don't end up partially visible.
Here is the relevant code I have so far:
document.querySelectorAll('nav a').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
const targetSection = document.querySelector(targetId);
const contentWrapper = document.querySelector('.content-wrapper');
const offsetLeft = targetSection.offsetLeft;
contentWrapper.scrollTo({
left: offsetLeft,
behavior: 'smooth'
});
});
});
const contentWrapper = document.querySelector('.content-wrapper');
let isScrolling;
contentWrapper.addEventListener('scroll', () => {
window.clearTimeout(isScrolling);
isScrolling = setTimeout(() => {
const sections = document.querySelectorAll('.content section');
const scrollPosition = contentWrapper.scrollLeft + contentWrapper.offsetWidth / 2;
let closestSection = sections[0];
let closestDistance = Math.abs(scrollPosition - closestSection.offsetLeft);
sections.forEach(section => {
const distance = Math.abs(scrollPosition - section.offsetLeft);
if (distance < closestDistance) {
closestSection = section;
closestDistance = distance;
}
});
closestSection.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'start'
});
}, 100);
});
.main {
height: 100%;
display: flex;
flex-direction: column;
}
.content-wrapper {
height: 80vh;
overflow-x: auto;
overflow-y: hidden;
scroll-snap-type: x mandatory;
position: fixed;
top: 20vh;
width: 100%;
scroll-behavior: smooth;
}
.content {
display: flex;
height: 100%;
}
.content section {
flex: 0 0 100vw;
scroll-snap-align: start;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.section-content {
padding: 20px;
background-color: rgb(69, 63, 189);
border-radius: 10px;
text-align: center;
width: 80vw;
}
<div class="main">
<div class="header">
<div class="headerWrap">
<div class="info">
<h1>Arnaud Ducoulombier</h1>
<h2>Full Stack Developer</h2>
<p>Motivated learner in the web industry</p>
</div>
<nav>
<ul>
<li><a href="#about">About</a></li>
<li><a href="#skills">Skills</a></li>
<li><a href="#projects">Projects</a></li>
</ul>
</nav>
</div>
</div>
<div class="content-wrapper">
<div class="content">
<section id="about">
<div class="section-content">
<h3>About Me</h3>
<p>This is the about section.</p>
</div>
</section>
<section id="skills">
<div class="section-content">
<h3>My Skills</h3>
<p>This is the skills section.</p>
</div>
</section>
<section id="projects">
<div class="section-content">
<h3>My Projects</h3>
<p>This is the projects section.</p>
</div>
</section>
</div>
</div>