Anchor links don’t reload the page, so the drawer never collapses. Here’s a one-liner (plus Tailwind-only extras) to make it behave.
1 · One-liner JavaScript fix
Drop this right before </body>
. It clicks the hamburger for you as soon as a visitor taps any in-page link.
<script>
/* Close the mobile nav after any in-page link is clicked */
document.querySelectorAll('.nav-menu a[href*="#"]').forEach(link => {
link.addEventListener('click', () => {
const btn = document.querySelector('.w-nav-button'); // hamburger
if (btn && getComputedStyle(btn).display !== 'none'
&& btn.getAttribute('aria-expanded') === 'true') {
btn.click(); // collapses drawer
}
});
});
</script>
2 · Tailwind-only polish
2.1 Smooth scroll + header offset
No custom CSS required — just utilities.
<!-- Layout root -->
<html class="scroll-smooth">
<!-- Each target section -->
<section id="mediagroups" class="scroll-mt-20">…</section>
<section id="enterprises" class="scroll-mt-20">…</section>
<section id="portfolio" class="scroll-mt-20">…</section>
<section id="contact" class="scroll-mt-20">…</section>
scroll-smooth
→scroll-behavior: smooth;
scroll-mt-20
→scroll-margin-top: 80px;
(20 × 4 px)
2.2 Highlight the link that’s in view (optional)
<script>
const sections = ['mediagroups','enterprises','portfolio','contact']
.map(id => document.getElementById(id));
const links = Object.fromEntries(
[...document.querySelectorAll('.nav-menu a[href*="#"]')]
.map(a => [a.getAttribute('href').replace('/#',''), a]));
const io = new IntersectionObserver(entries => {
entries.forEach(e => {
const id = e.target.id;
if (links[id]) {
links[id].classList.toggle('active-link', e.isIntersecting);
}
});
}, { threshold: 0.5 });
sections.forEach(s => io.observe(s));
</script>
<style>
/* Tailwind utility via @apply */
.active-link { @apply text-yellow-400; }
</style>
3 · Results in practice
- Menu closes instantly after a tap on mobile.
- Smooth scrolling respects the fixed header.
- Active-link highlighting keeps users oriented.
Copy → paste → publish — your single-page nav now behaves exactly the way users expect.
The One-Pixel Nightmare: When Your Mobile Menu Won’t Land in the Right Spot
You ship a single-page site, tap “Contact” in the hamburger menu, and watch the page almost scroll to the section—only to stop 50 px too high. I just burned an embarrassing amount of time on that bug. Here’s the short story so you don’t have to.
Symptoms
- First click on an anchor link is fine.
- Second, third, fourth clicks land the section half-hidden under your fixed header.
- Users think the page is “stuck” or broken.
Root Cause
- The browser jumps to
#section
immediately. - Your mobile drawer collapses after the scroll starts, changing the page height.
- A second click spawns another smooth-scroll animation on top of the first — overshooting every time.
The Two-Line Fix
$('.smooth-goto').on('click', function () {
$('html, body').animate(
{ scrollTop: $(this.hash).offset().top - 50 }, // 50 px = header height
1000 // 1-second glide
);
return false; // cancel native jump
});
this.hash
grabs the target ID directly from the link.- Subtract your header height (
- 50
). return false
stops the browser’s default jump, ensuring only one animation at a time.
Lessons Learned (the Hard Way)
- Pick one scroll engine.
Mixing Webflow’s smooth-scroll, Tailwind’s
scroll-smooth
, and jQueryanimate()
is a guaranteed way to stack animations. - Measure after the UI settles. If a drawer or cookie bar changes layout, calculate the offset after it’s closed.
- Don’t overthink it. Sometimes the fix is five lines of jQuery—once you understand the sequence of events.
I almost blamed Webflow, Tailwind, even my phone. Turned out the bug was just me letting two scroll systems fight each other.
If you’re battling the same issue, drop in the snippet above. One-pixel nightmares cured, time saved—no more hidden content for your users. Happy scrolling!
Comments
Post a Comment