Don't allow horizontal scroll when scrolling vertically (and vice versa)
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
and touchmove
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:
// Touchstart handler
if (scrollState === ScrollState.Default) {
// Record position and id of touch
touchStart = touch.location
touchStartId = touch.id.
scrollState = ScrollState.Touching
// If you have to manually scroll the div, first store its starting scroll position:
containerTop = $('.myContainer').scrollTop();
containerLeft = $('.myContainer').scrollLeft();
}
// Touchmove handler - If the user has dragged beyond a distance threshold,
// do the css classes swap.
if (touch.id === touchStartId && distance(touchStart, touch.location > threshold) {
scrollState = ScrollState.Scrolling;
swapCssClasses();
// If you have to manually scroll the div to prevent jump:
$('.myContainer').scrollTop(containerTop + (touch.location.y - touchStart.y));
// Do the same for horizontal scroll.
}
// Then, listen for debounced scroll events, like you're already doing,
// to reset your state back to default.
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.
Try this
HTML
<div
class="cu-dashboard-table__scroll-vertical"
>
<div
class="cu-dashboard-table__scroll-horizontal"
>
<!-- Scroll content here -->
</div>
</div>
CSS
&__scroll {
&-horizontal {
overflow-x: auto;
width: 100%;
-webkit-overflow-scrolling: touch;
}
&-vertical {
overflow-y: auto;
height: 100%;
-webkit-overflow-scrolling: touch;
}
}
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.
Why can't I scroll horizontally?
Why when I stop scrolling vertically, only then can I scroll horizontally?
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.