Open hdodov opened 5 years ago
I'm not sure this is something people would want given having such an event will necessarily disable all accelerated scrolling (at least if the page uses non-passive listeners for that).
@emilio just so we're on the same page - by accelerated scrolling you mean how Chrome for example increases deltaY
on the wheel
event from 100
to 200
, 300
or more, depending on how rapidly the user is scrolling?
If yes, then there might have been a misunderstanding. My idea was to be able to interrupt scrolling right before the UA changes scrollTop
. I mean, the UA should apply acceleration and other similar calculations. It should act as if the element is actually scrolling, except the scroll position doesn't change. If I scroll very rapidly, I should get larger deltas in the beforescroll
event but the element's scrollTop
should simply stay the same if defaultPrevented
is true
.
No, with accelerated scrolling I mean scrolling without going back to the main thread (async scrolling, etc).
Right now when you use your trackpad, or when you use the wheel and there are no wheel
event listeners, and keyboard (at least Firefox allows in some circumstances to use accelerated scrolling when you use the keyboard, in some situations), etc, scrolling is pretty fast, because it doesn't need to go through the main thread and it's a compositor-only effect.
Your event would mean that the UA cannot really scroll without dispatching an event and then knowing if that event is default-prevented.
From my understanding, this:
Right now when you use your trackpad, or when you use the wheel and there are no
wheel
event listeners [...]
Needs to be changed so that it checks if there are beforescroll
event listeners too, aside from wheel
listeners. If nothing is listening for beforescroll
, nothing can prevent scrolling and therefore, the browser can use accelerated scrolling?
You could also flag the beforescroll
listener as passive
, just as you can do with scroll
and still benefit from accelerated scrolling?
If there's a slow script running on the page, right now you can scroll. If the browser needs to wait for that script to finish in order to trigger the event (and run more script) in order to figure out if it can even start scrolling, it defeats the point of accelerated scrolling.
Making them passive would indeed work, but that prevents you from being able to preventDefault()
them.
From what I understand, having a non-passive wheel
event listener means the browser no longer uses accelerated scrolling (since it needs to wait for the event handler to see if it should further scroll).
Well, can't it be the same for the beforescroll
event too? By adding a non-passive beforescroll
listener, you opt-out of accelerated scrolling in exchange for better scroll control. It's a decision that the developer makes. The UA should simply give that choice.
If I have a passive wheel
listener and no beforescroll
listeners, I should benefit from accelerated scrolling since there's still nothing to prevent the scroll from happening. It should act as the same way as if my proposed event was not implemented.
I think the passive
option of events was designed to solve that very problem you're talking about. You won't have better scrolling performance with a non-passive beforescroll
the same way you won't have it with a non-passive wheel
listener. By specifying that the event is passive, you tell the browser that it can assume nothing will prevent scrolling so it can optimize it. Why can't beforescroll
function the same way?
Sure, it can be a passive-by-default event or what not, and behave like you describe, I'm just unsure if people are going to be keen to add a potential performance footgun like that.
I think that people would very much prefer sacrificing this performance benefit over using hacks, workarounds, overflow, and whatnot to "prevent" the browser from scrolling or using position:fixed
so that content can appear like it's not scrolling.
Accelerated scrolling is a performance benefit after all. If you use beforescroll
wisely, it shouldn't be such a problem. Besides, beforescroll
is useful for passive functionality as well, thanks to its delta properties and the fact that whatever you do with it won't lag behind.
My first issue here. I'll be making a suggestion for CSSOM View Module Events.
Problem
We have solid control over various input events. For keyboard, we have
keydown
,keyup
,keypress
(deprecated). For mouse, we haveclick
,dblclick
,mouseup
,mousedown
... We don't have great control over scroll, though.The
scroll
event is useful for implementing functionality that responds to scrolling, but is too limited for more advanced use cases. It's emitted after the actual scrolling has taken place and therefore doesn't provide much control. You can'tpreventDefault()
and prevent scrolling and if you usedocument.scrollingElement.scrollTop
for scroll-based animations, for example, they can be glitchy since they would lag one frame.Touch events and the
wheel
event might be used as an alternative. They fire before scrolling has taken place, but they don't fire continuously. They can only be used to know when scrolling is expected to start, which is not very helpful.Solution
A middle ground between the
scroll
andwheel
events is what's needed. My suggestion is to have thebeforescroll
event which:deltaY
/deltaX
properties, similar towheel
defaultPrevented
which prevents scrollThe delta properties specify with how much pixels the element will be scrolled in the current event loop. Or, in other words,
element.scrollTop + event.deltaY
in thebeforescroll
event should be equal toelement.scrollTop
in thescroll
event. This is useful because:scroll
mentioned abovescrollTop
between thescroll
event emissions (which also lag one frame)You can call
preventDefault()
to prevent scrolling, but not any followingbeforescroll
events. In Chrome, this code:...would log similar to the following after a single
wheel
event withdeltaY
of100
:Here's a fiddle. With
beforescroll
, you should be able to do this:...and the scroll target should not be scrolled, while the logs should read:
Since the actual scroll was prevented,
scroll
events should not fire after thebeforescroll
handler.Why
Having this event would allow developers to implement advanced behaviors based on scrolling accurately. It would also give a reliable way to prevent scrolling, which is currently not easily achievable. You could prevent scrolling with the mouse wheel by calling
preventDefault()
on thewheel
event, but you can still scroll with the scrollbar, arrow keys,scrollTo()
or even clicking a link with a hash.Use cases:
beforescroll
to prevent vertical scrolling and use the delta properties to scroll that section horizontally. When the section is fully scrolled, vertical scroll is no longer prevented and the user can continue scrolling the site. This is a UX pattern for carousel-like content.beforescroll
could allow for better hijacking which, in the right hands, can improve UX. For example, while the site visitor scrolls through a section, the developer could usebeforescroll
to prevent scrolling, optionally animate something inside the section withdeltaY
, and usedocument.scrollingElement.scrollTop += Math.ceil(event.deltaY / 2)
. This would still scroll the page vertially, with the UA's expected scroll easing, only at half the speed. This way, the developer can easily implement dynamic visuals for a content section and then emphasize them by directing the user's attention with slower scrolling.beforescroll
to implement scroll-triggered animations via the delta properties. This would be easier and more accurate compared to manually measuring delta with ascroll
handler.Edit: This event would also open possibilities for carousel libraries on mobile. They could use
overflow:auto
and control the behavior withbeforescroll
, instead of settingoverflow:hidden
and manually scrolling the target by monitoring touch events.