I have a list, with the overflow-x
and overflow-y
set to auto
. In addition, I've set up momentum scroll, so the touch scrolling works nice in mobile, using webkit-overflow-scrolling: true
.
The issue, however, is that I cannot figure out how to disable the horizontal scroll when scrolling vertically. It leads to really bad user experience, as the swiping towards the top left or top right will cause the table to scroll diagonally. When the user is scrolling vertically, I absolutely do NOT want any scrolling horizontally until the user has stopped scrolling vertically.
I've tried the following:
JS:
offsetX: number;
offsetY: number;
isScrollingHorizontally = false;
isScrollingVertically = false;
//Detect the scrolling events
ngOnInit() {
this.scrollListener = this.renderer.listen(
this.taskRows.nativeElement,
'scroll',
evt => {
this.didScroll();
}
);
fromEvent(this.taskRows.nativeElement, 'scroll')
.pipe(
debounceTime(100),
takeUntil(this.destroy$)
)
.subscribe(() => {
this.endScroll();
});
}
didScroll() {
if ((this.taskRows.nativeElement.scrollLeft != this.offsetX) && (!this.isScrollingHorizontally)){
console.log("Scrolling horizontally")
this.isScrollingHorizontally = true;
this.isScrollingVertically = false;
this.changeDetectorRef.markForCheck();
}else if ((this.taskRows.nativeElement.scrollTop != this.offsetY) && (!this.isScrollingVertically)) {
console.log("Scrolling Vertically")
this.isScrollingHorizontally = false;
this.isScrollingVertically = true;
this.changeDetectorRef.markForCheck();
}
}
endScroll() {
console.log("Ended scroll")
this.isScrollingVertically = false;
this.isScrollingHorizontally = false;
this.changeDetectorRef.markForCheck();
}
HTML:
<div
class="cu-dashboard-table__scroll"
[class.cu-dashboard-table__scroll_disable-x]="isScrollingVertically"
[class.cu-dashboard-table__scroll_disable-y]="isScrollingHorizontally"
>
CSS:
&__scroll {
display: flex;
width: 100%;
height: 100%;
overflow-y: auto;
overflow-x: auto;
will-change: transform;
-webkit-overflow-scrolling: touch;
&_disable-x {
overflow-x: hidden;
}
&_disable-y {
overflow-y: hidden;
}
}
But the everytime I set overflow-x
or overflow-y
to hidden
when its been scrolled, scrolling will glitch and jump back to the top. I've also noticed that webkit-overflow-scrolling: true
is the reason why this occurs, when I remove it, the behavior seems to stop, but I absolutely need this for momentum scrolling in mobile devices.
How do I disable horizontal scroll when scrolling vertically?
this looks like fun ;)
I won't argue about if it's reasonable.
I tried it with RxJS:
We buffer every 4th
touchemove
event and then make some highly sophisticated calculation with the coordinates of the four events (map(calculateDirection)
) which outputsRIGHT
,LEFT
,UP
orDOWN
and based on that I try to disable vertical or horicontal scrolling. On my android phone in chrome it kind of works ;)I'v created a little playground, which could be enhanced, rewritten, whatsoever ...
Cheers Chris
It's generally a bad practice for your design to need multiaxis scrolling on mobile, unless maybe you're showing big tables of data. That being said, why do you want to prevent it? If a user wants to scroll diagonally, that doesn't seem like the end of the world to me. Some browsers, like Chrome on OSX, already do what you're describing by default.
If you must have single-axis scrolling, a possible solution might be to keep track of the scroll position yourself via
touchstart
andtouchmove
events. If you set your drag threshold lower than the browser's, you may be able to do your css stuff before it starts scrolling, avoiding the perceived glitch. Also, even if it does still glitch, you have the touch start and the touch's current location. From these, if you record your div's starting scroll position, you can manually scroll the div to the correct place to counteract it jumping to the top if you have to. A possible algorithm might look like this:Second idea: in your scroll handler, instead of changing the css classes, set the scroll position of the div directly for the axis you want locked. IE, if you're scrolling horizontally, always set the scrollTop to its starting value. This might also cancel scrolling, not sure. You'd have to try it to see if it works.
Need to use three containers.
In the first container I enable vertical scrolling and disallow horizontal.
In the second, vice versa, I enable horizontal scrolling and disallow vertical. Be sure to use
overflow-x: hidden;
andoverflow-y: hidden;
, otherwise child containers may go beyond the current container.Also need to use
min-width: 100%; min-height: 100%;
.For the third container we need to use
display: inline-block;
and then the internal content will stretch this container and the corresponding scroll bars will appear on the two parent blocks.HTML
CSS
You can test it here in Safari on iPhone.
Good luck!!
Try this
Instead of using one div for scrolling, why dont you use two? Have one for X and one for Y
There are numerous ways to try to solve this. Some good ideas are offered here.
However, as others have alluded, relying on (or trying to avoid) multi-dimensional scrolling is a bad UX smell - indicative of the UX being a problem. I don't think this is a legitimate dev issue. It would be better to take a step back and reevaluate what you're trying to accomplish. One of the issues might be that in an effort to make the UI more usable, you'll be confusing users. The usability described here would likely cause confusion.
These are some questions that may be asked (inaudible).
If you're trying to allow for additional information of the vertical data list to be browsable only when the user has selected the row, it would likely be much better to simply have a flat list by default only scrollable vertically, and only toggle the vertical information when the "row" has been selected/activated. Collapsing the details would get you back to the flat vertical list.
If you're having to jump through hoops to solve such a fringe technical challenge, it's a good indication that the user experience has not been designed well to begin with.