Open Bug 1090028 Opened 10 years ago Updated 2 years ago

Nested scrollbars don't propagate

Categories

(Core :: XUL, defect)

34 Branch
x86
macOS
defect

Tracking

()

UNCONFIRMED

People

(Reporter: willmarquardt, Unassigned)

Details

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:34.0) Gecko/20100101 Firefox/34.0
Build ID: 20141023111813

Steps to reproduce:

Scrolled to the bottom of a scrollable element, then scrolled more


Actual results:

Nothing


Expected results:

Once you reach the limit of a nested scrollable area, the scroll event should propagate to the greater context. Unless the mouse is moved, this doesn't happen.
This is by design. Why do you prefer the scroll to propagate?
What device are you using for scrolling?
I've tested using the Apple magic mouse and my macbook trackpad. The desired behavior that I described in Comment 0 is present both in Chrome and Safari. 

Benefits of FF behavior: you don't overscroll when you solely intended to go to the end of the scrollable element.
Detriments of FF behavior: UI feels unresponsive as user continuously tries to scroll down page. Sometimes scrollable elements are the full width of the page, and moving the mouse outside the scrollable element (the intuitive solution) is difficult. The solution of slightly moving the mouse is unintuitive.

My view is that the other browsers' implementation is more intuitive and feels responsive, but


Solution - better than either current model would be this:
--------

Use a debounce function with a 200ms delay on the scroll event. If new scroll events come in before that 200ms threshold, they are considered the *same* scroll event, the debounce timer is reset, and current FF behavior prevails. Any scroll event that comes in after that threshold is considered a *new* scroll event, and the scroll event is propagated to the greater context. This is responsive, intuitive, and prevents the overscroll effect that FF's default behavior seeks to prevent.

NB:
Many new scroll devices (or maybe it's a software implementation) use inertial scroll in that they keep firing a scroll event after the user has released the device. In FF, if I scroll hard inside a scrollable element, wait a second, then move the mouse, the whole window starts scrolling. This is, in my view, unquestionably undesired.

My debounce function would have a hard time discerning *same* and *new* scroll events. The only way of doing so would be to measure the frequency of scroll events and consider a spike in frequency to be a *new* event and thus a reason to propagate, but I'm not sure how resource-intensive that would be. 

Overall, I still think the debounce solution would be easily implemented and desirable to all users.
These are good suggestions, thank you.

We already have a debounce delay, but it's 1500ms (configurable as mousewheel.transaction.timeout). We could reduce that.

And for scroll devices that group scroll motions as gestures, like the Magic Mouse and Apple touchpads, we could just always reset the wheel transaction as soon as the fingers are lifted from the device (and any inertial scroll has completed).

Masyuki, what do you think?

(In reply to willmarquardt from comment #2)
> NB:
> Many new scroll devices (or maybe it's a software implementation) use
> inertial scroll in that they keep firing a scroll event after the user has
> released the device. In FF, if I scroll hard inside a scrollable element,
> wait a second, then move the mouse, the whole window starts scrolling. This
> is, in my view, unquestionably undesired.

Absolutely. We have a few bugs about similar issues, we need to fix them (bug 605751, bug 661603, bug 926386). But what you mentioned affects the Magic Mouse more than e.g. Macbook touchpads because the touchpads automatically decelerate scroll events to a quick stop as soon as the mouse is moved.
Flags: needinfo?(masayuki)
Cool, didn't know about mousewheel.transaction.timeout. This acts more like a throttle than a debounce in that a debounce requires the events to stop firing before it starts the delay. I think a debounce would make more sense in as it can better distinguish between two separate scroll actions and allows for a much shorter timeout. 

>we could just always reset the wheel transaction as soon as the fingers are lifted from the device (and any inertial scroll has completed)

This would be great. Ideally, the transaction would also be reset once the bottom (assuming scroll downwards) of the container is reached. That way, we don't need to wait for the inertial scroll to complete (sometimes over a second) before being able to scroll the page. It would just be scroll, lift finger, scroll, which would be perfect.
(In reply to willmarquardt from comment #4)
> Cool, didn't know about mousewheel.transaction.timeout. This acts more like
> a throttle than a debounce in that a debounce requires the events to stop
> firing before it starts the delay.

You're right, I was misremembering how the code worked. I agree that a proper debounce would be more appropriate.
(In reply to Markus Stange [:mstange] from comment #3)
> We already have a debounce delay, but it's 1500ms (configurable as
> mousewheel.transaction.timeout). We could reduce that.

Yeah, but I think that pages designed with nested scrollable area is not so major. And I tested various delay values at implementing it. So, if we use 200ms or something simply, it could have another side effect.

# Basically, nesting scrollable areas is bad design...

> And for scroll devices that group scroll motions as gestures, like the Magic
> Mouse and Apple touchpads, we could just always reset the wheel transaction
> as soon as the fingers are lifted from the device (and any inertial scroll
> has completed).

Hmm, it might be doubt but I'm not sure... When touchpad doesn't have enough height or width to scroll to the end of a scrollable area, user uplifts fingers and down them repeatedly. So, if user doesn't realize that scroll already reaches its end, a repeated operation scrolls its ancestor unexpectedly.

I wonder, it's better to use check how many times operated to scroll to impossible direction. If we can detect the count of operations to retry to scroll, it must let us know when user tries to scroll another scrollable area.
Flags: needinfo?(masayuki)
> Yeah, but I think that pages designed with nested scrollable area is not so major. And I tested various delay values at implementing it. So, if we use 200ms or something simply, it could have another side effect.
First, you'll notice that this exact quote *is* a nested scroll if you make your window norrower than 1000px, which isn't uncommon.

Second, as stated in Comment 4, the key problem is not the delay number, it's the fact that it's a *throttle* function instead of a *debounce* function. 

> user uplifts fingers and down them repeatedly. So, if user doesn't realize that
> scroll already reaches its end, a repeated operation scrolls its ancestor unexpectedly
This is why you use a debounce function. "repeatedly" insinuates that there is some small interval between scrolls. That interval is much smaller than the default 1500ms.

We need a debounce function set to that interval instead of a throttle function set to a much higher interval to compensate for the fact that using a throttle is a poor design choice.
Flags: needinfo?(masayuki)
Component: Untriaged → XUL
Product: Firefox → Core
When I try to use Google Scholar, I encounter a number of extra scrollbars, one scrollbar per result. I have coordination problems and use scrolling software, and the extra scrollbars break my scrolling software on Google Scholar. This fix would solve my problem there.
Sorry, I forgot to reply to this.

I wonder, rather than we simply reduce the "mousewheel.transaction.timeout" value, it might be better to abort current transaction when user fails to scroll specific amount.  For example, we could assume that the user couldn't scroll with wheel events whose delta values reaches about 10 or 20 lines, the result isn't expected by the user.

However, I don't have much time to work on this. If somebody will post patches, I'd like to review them.
Flags: needinfo?(masayuki)

This should also apply to Page Down.

Related:

Bug 1673085 for extra scrollbars appearing on Google Analytics. Hardly the only affected site.

Bug 1084121 for something involving wheels.

Bug 1699955 for showing focus, making it easier to see what's blocking scrolling or Page Down.

Is bug 1040226 involved? I can't get mozregression to work that far back. https://hg.mozilla.org/releases/mozilla-b2g32_v2_0/rev/611a9dae1168

Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.