In my Javascript code I'm listening for the scroll event and doing some work whenever it fires:
document.addEventListener(
'scroll',
(event) => {
// do something
},
{ passive: true }
);
I have a nav bar which contains anchors to different sections on my page, which when clicked causes the page to scroll down to the section (and fires the scroll event):
<nav class="navbar">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link nav-link-jump" href="#anchor-1">Popular Items</a>
</li>
</ul>
</nav>
...
<section id="anchor-1">...</section>
However, I do not want the scroll event to fire when the page scrolls as a result of clicking on the anchor link. I only want it to fire when the user actually scrolls (i.e. with mouse wheel or by swiping).
I couldn't find any field in the 'event' object that would differentiate the two scroll sources but perhaps I missed something.
The scroll event takes the most generic Event interface so there are no properties for differentiating the scroll source. I think it is basically unfeasible without completely overriding the default scroll anchor behaviour with your own JS. Having tried to do similar things in the past and ended up with horrible results, I would not recommend it.
You might want to think about doing this in reverse: disable anything you don't want to happen when a user clicks on the link, then renable it the next time a wheel or touchmove event is triggered. These events are particular to user scroll behaviour and won't be triggered by an anchor link.
const link = document.querySelector("a");
const counter = document.querySelector("p");
let userScrolling = true;
link.addEventListener("click", () => userScrolling = false);
window.addEventListener("wheel", () => userScrolling = true);
window.addEventListener("touchmove", () => userScrolling = true);
window.addEventListener("mousedown", () => userScrolling = true);
window.addEventListener("scroll", () => {
if (!userScrolling) return;
counter.textContent = parseInt(counter.textContent) + 1;
});
html {
scroll-behavior: smooth;
}
p {
position: fixed;
bottom: 0;
right: 0;
padding: 16px;
}
.separator, #target {
height: 100vh;
}
<a href="#target">Link</a>
<p>0</p>
<div class="separator"></div>
<div id="target">Target<div>
You'll notice that there's a mousedown handler in there too. That's so we can enable user scrolling again by manually clicking and dragging the scrollbar, and also by clicking the middle-wheel compass. Unfortunately either one or multiple scroll events will still slip through depending on the browser if you use mousedown while the anchor scroll is in progress.
You could also go a step further to alleviate this issue and make sure the mouse event is from a middle wheel button event.button = 1, or is triggered over a scrollbar. This second factor is potentially complicated and beyond the scope of the question so I leave you with this answer which is the most promising I could find.
Honestly though, I think the takeaway here should be don't try to do this. The reason you can't distinguish user/anchor in the event is because they are functionally identical - a user clicking a button to scroll for them is user scroll behaviour and it should not realistically cause a problem. If you have some scroll-based animation you need to skip over, it might be better to just remove smooth-scrolling behaviour instead and reset the animation values in the background.
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