8000 [cssom-view][proposal] beforescroll event · Issue #4172 · w3c/csswg-drafts · GitHub
[go: up one dir, main page]

Skip to content
8000

[cssom-view][proposal] beforescroll event #4172

@hdodov

Description

@hdodov

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 have click, 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't preventDefault() and prevent scrolling and if you use document.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 and wheel events is what's needed. My suggestion is to have the beforescroll event which:

  • is fired right before the element is actually scrolled
  • has deltaY/deltaX properties, similar to wheel
  • has defaultPrevented which prevents scroll

The 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 the beforescroll event should be equal to element.scrollTop in the scroll event. This is useful because:

  • it allows you to predict the next scroll position of the target element, fixing the lag issue with scroll mentioned above
  • you can easily know the difference in scroll between frames, accounting for the various animations/easings of UAs, without having to compare scrollTop between the scroll event emissions (which also lag one frame)

You can call preventDefault() to prevent scrolling, but not any following beforescroll events. In Chrome, this code:

var delta = null
window.addEventListener('scroll', function (e) {
  var st = document.documentElement.scrollTop
  console.log(st - (delta || st))
  delta = st
})

...would log similar to the following after a single wheel event with deltaY of 100:

0
5
7
10
13
14
15
12
10
8
4
1

Here's a fiddle. With beforescroll, you should be able to do this:

window.addEventListener('beforescroll', function (e) {
  console.log(e.deltaY)

  // animate some element with deltaX/deltaY
  e.preventDefault()
})

...and the scroll target should not be scrolled, while the logs should read:

5
7
10
13
14
15
12
10
8
4
1

Since the actual scroll was prevented, scroll events should not fire after the beforescroll 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 the wheel event, but you can still scroll with the scrollbar, arrow keys, scrollTo() or even clicking a link with a hash.

Use cases:

  1. If the site visitor scrolls to a section with horizontal overflow, the developer could use 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.
  2. Better "scroll hijacking." Even though that's considered bad practice, 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 use beforescroll to prevent scrolling, optionally animate something inside the section with deltaY, and use document.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.
  3. Similar to the previous use case, the developer could speed up (instead of slowing down) the scrolling of a page that has more whitespace as a part of it's design.
  4. For use cases where you don't want to disturb the natural page scrolling, you can still use beforescroll to implement scroll-triggered animations via the delta properties. This would be easier and more accurate compared to manually measuring delta with a scroll handler.

Edit: This event would also open possibilities for carousel libraries on mobile. They could use overflow:auto and control the behavior with beforescroll, instead of setting overflow:hidden and manually scrolling the target by monitoring touch events.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0