Reputation: 619
I would like to get one event only per scroll event
I try this code but it produces "wheel" as many times the wheel event is triggered. Any help? Thank you
window.addEventListener("wheel",
(e)=> {
console.log("wheel");
e.preventDefault();
},
{passive:false}
);
Use case (edit) I want to allow scrolling from page to page only - with an animation while scrolling. As soon I detect the onwheel event, I would like to stop it before the animation finishes, otherwise the previous onwheel continues to fire and it is seen as new event, so going to the next of the targeted page
My conclusion : It is not possible to cancel wheel events. In order to identify a new user wheel action while wheeling events (from a former user action) are on going, we need to calculate the speed/acceleration of such events
Upvotes: 12
Views: 18107
Reputation: 321
Event.preventDefault()
tells the browser not to do the default predefined action for that event, such as navigating to a page or submitting the enclosing form, etc. It does not necessarily prevent events from firing.
Also, there is a difference between the wheel
event and the scroll
event. The wheel
event is fired when the user rotates a wheel button, and the scroll
event is fired when the target's scrollTop
or scrollLeft
property is changed due to the scroll position being changed.
When the user rotates the wheel button, the wheel
event is fired before any scroll
events that could be fired. However, the wheel
event might not result in any scroll
event simply because the pointer is not hovering on any element or the element is not scrollable at the moment.
To aggregate quickly repeated function calls to the event handler, you can debounce the event handler function. The idea is to wait a certain amount before committing to the action. When a function is debounced, it becomes a new function, when called, sets off a timer that calls the wrapped function inside. The timer is reset and restarted when debounced function is called again. Look at the example diagram below.
© Ilya Kantor(https://github.com/javascript-tutorial/en.javascript.info, licensed under CC-BY-NC)
The function f
is a debounced function with a 1000ms timeout duration and is called at time instants 0, 200ms, and 500ms with arguments a
, b
, and c
, respectively. Because f
is debounced, calls f(a)
and f(b)
were "not committed/ignored" because there was another call to f
within a 1000ms duration. Still, call f(c)
was "committed/accepted" at the time instant 1500ms because no further call followed within 1000ms.
To implement this, you can use the setTimeout
and clearTimeout
functions. The setTimeout
function accepts an action(code or function to execute) and delay in milliseconds, then returns a timer ID in integer. The given action will be executed when the timer expires without being canceled.
const timerId = setTimeout(action, delay)
The clearTimeout
function could then be used to destroy the timer with a given ID.
clearTimeout(timerId)
Following simple debounce implementation could be used:
// Default timeout is set to 1000ms
function debounce(func, timeout = 1000) {
// A slot to save timer id for current debounced function
let timer
// Return a function that conditionally calls the original function
return (...args) => {
// Immediately cancel the timer when called
clearTimeout(timer)
// Start another timer that will call the original function
// unless canceled by following call
timer = setTimeout(() => {
// Pass all arguments and `this` value
func.apply(this, args)
}, timeout)
}
}
Read more: Default parameters, Rest parameters, Function.apply()
, this
keyword
To use is quite simple:
eventTarget.addEventListener('wheel', debounce((e) => {
console.log('wheel', e)
}))
This will limit console.log
calls to whenever a wheel
event has not been fired in a second.
Live example:
function debounce(f, d = 99, t) {
return (...a) => {
clearTimeout(t)
t = setTimeout(() => {
f.apply(this, a)
}, d)
}
}
document.addEventListener('wheel', debounce((_) => {
console.log('wheel')
}))
A more modern approach uses Promise
on top of this idea.
Upvotes: 4
Reputation: 15831
This is fairly simple problem, store anywhere the last direction and coditionally execute your code:
direction = '';
window.addEventListener('wheel', (e) => {
if (e.deltaY < 0) {
//scroll wheel up
if(direction !== 'up'){
console.log("up");
direction = 'up';
}
}
if (e.deltaY > 0) {
//scroll wheel down
if(direction !== 'down'){
console.log("down");
direction = 'down';
}
}
});
Anyway, the UX context should be defined. May be that throttling or debouncing your function will give better results in some scenarios.
Throttling
Throttling enforces a maximum number of times a function can be called over time. As in "execute this function at most once every 100 milliseconds."
Debouncing
Debouncing enforces that a function not be called again until a certain amount of time has passed without it being called. As in "execute this function only if 100 milliseconds have passed without it being called.
In your case, maybe debouncing is the best option.
Temporary lock the browser scroll
$('#test').on('mousewheel DOMMouseScroll wheel', function(e) {
e.preventDefault();
e.stopPropagation();
return false;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="test">
<h1>1</h1>
<h1>2</h1>
<h1>3</h1>
<h1>4</h1>
<h1>5</h1>
<h1>6</h1>
<h1>7</h1>
<h1>8</h1>
<h1>9</h1>
<h1>10</h1>
</div>
Upvotes: 5
Reputation: 11364
You could set a minimum amount of time that must pass before you consider an additional scroll event as actionable.
For example, below, 3 seconds must pass between scroll events before console.log("wheel")
is fired again:
function createScrollEventHandler(milliseconds)
{
let allowed = true;
return (event)=>
{
event.preventDefault();
if (allowed)
{
console.log("wheel");
allowed = false;
setTimeout(()=>
{
allowed = true;
},milliseconds);
}
}
}
let scrollEventHandler = createScrollEventHandler(3000); // 3 seconds
window.addEventListener("wheel",scrollEventHandler);
Upvotes: 0
Reputation: 611
You almost had it But you need to wrap your code in a function. I added some extra little bits so you can differentiate up and down :)
//scroll wheel manipulation
window.addEventListener('wheel', function (e) {
//TODO add delay
if (e.deltaY < 0) {
//scroll wheel up
console.log("up");
}
if (e.deltaY > 0) {
//scroll wheel down
console.log("down");
}
});
How it works?
(e) = This is just the event, the function is triggered when ever you scroll up and down, but without the function event it just doesn't know what to do! Normally people put "event" but im lazy.
deltaY = This is a function of the wheel scroll it just makes sure you scrolling along the Y axis. Its a standard inbuilt function there is no external variables you need to add.
Extras
setTimeout
You could add this. In the if statements as @Lonnie Best suggested
Upvotes: 0