Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I will keep saying this every single time some one gets this wrong (which is at least 49 times out of 50): the only way to handle scrolling properly on the web is for your scroll events to fall onto a real scrollable area, and to observe the effect it has (e.g. apply the scroll position to what you’re rendering with). This can be tricky to achieve well when you have dynamic content that the mouse needs to be able to interact with within that area, but it is possible to do well, with careful application of `position: sticky` and/or `position: fixed`.

As it stands, scrolling is nigh unusable on my Surface Book: vertical scrolling goes at a million times the speed it should, and horizontal scrolling goes a million times as fast as it should in the wrong direction.

Don’t do scrolljacking. It’s bad, every single time. With the tools the web gives you it’s not possible to get it completely right that way.



Absolutely. In the development process of our product DataGridXL we've also experimented a lot with "scrollwheel" events and such, for months really. It's just not possible to get it to feel natural. You might be able to get it working nicely on one specific OS+browser combo, but that's about it.

Always listen to the native "scroll" event: https://www.datagridxl.com/demos/one-million-cells


One deficiency of using the native scrollbars visually here is that the arrows at the end of the scroll bars on some platforms (e.g. Windows, most Linux) will scroll by a predetermined number of pixels, more or less, whereas in such an application you would probably prefer it to scroll one column horizontally or one row (though maybe more than one) vertically. Of course, placing your own scrollbar visuals is a hazard too, most significantly because it doesn’t look native and may offer different functionality from the normal scrollbar. You can’t win, the web doesn’t give you the tools you need to get the experience perfect.

But yeah, your example in DataGridXL is a good example of the right approach to take for this if you want scrolling to be done in steps as is conventional in spreadsheets.


> whereas in such an application you would probably prefer it to scroll one column horizontally or one row (though maybe more than one) vertically.

But isn't this exactly what element.scrollIntoView() is supposed to be for?

Native scrollbars, styled with scrollbar-width and textareas resizing with cols attribute is pretty straight forward if you do the box-sizing right.


I think you’re misunderstanding. I’m saying that on platforms where the native scrollbar has arrows that you can click to scroll by a small amount in the nominated direction, you might prefer clicking on those arrows to do something different—to scroll one row or one column, whatever distance may be, rather than however many pixels the user agent decides. But you can’t do that on the web.

element.scrollIntoView() is completely unrelated to this.


Ah gotcha, yes you are indeed correct. Something like custom amount of lines to scroll isn't possible afaik.

I was just stating that with scrollIntoView and the element's scroll-snap-align and scroll-padding/scroll-margin CSS properties you could set the wanted offsets and alignments of the scrollbar and define what you were argueing about.

But of course, you cannot influence the behaviour of the scrollbar arrows themselves.

[1] https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap...


Ah, CSS scroll snapping. Refer to my other comment in this thread about that, I strongly recommend against using it for layout matters like this, it messes with the scrolling behaviour (especially on devices with precise-scrolling touchpads and inertia) something fierce; it’s quite unavoidably every bit as bad as doing the scrolljacking in JS.


You know your stuff, I can tell! In a pre-release version, we had hidden the native scrollbar and put our own scrollbar graphic on it that would animate using CSS transform according to native scroll offsets vs. DGXL viewport dimensions. The scrollbar size (width/height) and position made more sense, but at the end we couldn't get it to feel quite right.

We're quite happy with the native scrollbar. It's fine on mobile too.


Yep, I think native scrollbars is reasonable for DGXL. Maybe eventually you’ll sniff whether scrollbars take space and draw your own that match the size if they do, but it’s definitely a little risky.

These sorts of things are particularly interesting once you support a sparse spreadsheet; drawing your own scrollbars becomes much more compelling then, so that you can make them more useful. No idea about Excel, but LibreOffice Calc—(which amusingly gets scrolling by precise touchpad wrong in just the same way as Luckysheet, sans the wrong horizontal direction; this stuff is hard everywhere)—well, LibreOffice Calc’s scrollbars essentially pretend the whole time that the document is only as large as the extent of the cells with content or the selection, whichever is larger in each axis, plus a screenful or so in each axis. So if you’re at A1 of an empty document, the scrollbars thumbs fill about half of the bar, but if you’re at AMJ1048576 (maximum sheet dimensions are 2¹⁰×2²⁰), the thumbs are tiny at the bottom and right.


Sounds like a very strange approach, those LibreOffice Calc scrollbars ;-) Sometimes it's useful to give a minimal width/height for a scrollbar thumb, so that it remains visible and selectable. A tiny scrollbar thumb might be "realistic" when it represent the viewport vs document dimension, but it always looks a bit stupid.

How come you know so much about scroll bars? Did you make your own spreadsheet app/component?


The spreadsheet approach makes a lot of sense: they’re dealing with what’s practically a near-infinite document (a million cells is close enough to infinity in such cases!), but people seldom actually use that many cells. Thus, the shenanigans they pull are to make the scrollbars useful, so that they typically give a fair reflection of the actual size of the document the user is dealing with. I imagine Excel does something similar.

I know so much mostly just because I observe carefully, in both desktop and web software. I haven’t implemented anything like this, but when I observe things imperfect on the web and it’s a type of imperfection I’m not familiar with, I often assess it to figure out whether it’s possible to get right. I also value getting things right in straight HTML/CSS with no JavaScript.


Your implementation is pretty neat.

We recently kind of went down the path of our own scroll bar in one of our app forms because users wanted the scroll to behave in a very specific way. It was impossible to make it work as expected in all the OS and browsers. We eventually rolled back the change, restored the native scroll bar and instead provided a big optional floating scroll button in the bottom corner of the app which user can choose to use to scroll the way they expected to. Kept everyone happy.


Thanks, we really wanted to get the scrolling right so I'm glad it shows.

> We eventually rolled back the change, restored the native scroll bar

Doesn't it feel good to remove a bunch of "hacky code" and just go back to native? Even if it's not 100% like you imagined, at least you know that the other "perfect solution" never really existed.


The way I solved this in my own (not finished) web-based spreadsheet app is to have a scrollable div positioned over the canvas and to size the transparent div contents to the width/height of the sheet. Then I register scroll event handlers and redraw the canvas appropriately when the scroll changes. I think this is how Google sheets works, although it's a bit difficult to tell for sure.

It behaves exactly how you would expect. It even properly handles clicking and dragging to the edge of the screen, which I did not expect prior to implementing it.


It seems difficult for a canvas-based spreadsheet to do native scrolling. The DOM is drawn in advance and there is no cost to scroll. But canvas is different, every frame of scrolling means calculation. In this case, the use of native scrolling will cause jamming and poor experience.


With infinite scrolling, you can also just set the container to be absolutely huge, and use the scroll position to index. This works especially well for computed layouts like tables, where culling rows and columns is a simple scan over run lengths of row and column sizes.

Likewise if you know things will be inserted at the top of a scroll view, you can pre-allocate above the “top”.

The one thing that is hard to do well (but less hard now with new CSS features I think) is scrolling that feels good but always ends with the border of a cell being aligned to an edge of the scroll view (the Excel behaviour).


CSS Scroll Snapping may have a place for image gallery sorts of widgets, but for broader layout matters I strongly recommend against it: it can work tolerably on mobile (though it’s not without problems), but on desktop it seriously messes with scrolling so that on many devices it’s almost entirely problems.

(That’s what I said at https://news.ycombinator.com/item?id=23915285 which was dealing with much the same area.)

Instead, you’ll want to implement such snapping with absolute snapping as seen with robbiejs’s example.


Do you happen to have some links to resources I could read a bit more about it?


There are some intricacies about when to reset the scroll position, if ever.

Here's one way: https://codepen.io/xorgy/pen/yYdVoY


This way is very, very wrong in how it resets the scroll position: it essentially assumes synchronous scrolling, and falls apart completely if that isn’t the case—and it hasn’t been for some years.

By “synchronous scrolling” I mean that reading scrollTop gets you the true current value, and that setting scrollTop sets the value immediately. These used to be the case, but scrolling is handled off-thread now, so the values you read may not be the actual values, and your setting the value may clobber scrolling that has taken place since your code started being called. (This also means that any fake scrolling you do will lag behind reality—one of the reasons to use fully native scrolling if you possibly can. This has been unavoidable since asynchronous scrolling came in.)

The effect is that on devices with precise scrolling and inertia especially, it will make scrolling extremely slow, because essentially most of the scrolling delta is being lost. I see this problem very commonly in systems that have tried to implement their own scrolling. For reference, I’m using Firefox on a Surface Book. A generous two-finger swipe that should scroll by several thousand pixels is in your demo (when further instrumented to keep totals of deltas) only perceiving several dozen pixels of delta.

In short: you mustn’t ever reset the scroll position.


On Chrome based browsers on Android at least, it works perfectly fine with momentum. It even does accumulating momentum, I can easily get it up to 6,000px/frame.

Does this not work properly on iOS?


Although in theory the problem is universal, in practice the problem only appears when the user is using touchpad-based scrolling; touch-based scrolling is typically unaffected.

But it’s all happening because you’re interacting with the scroll position in a way that is incorrect within the models of the platform, reading and writing the scroll position as though it were atomic and sequentially consistent when it’s not, so it could break in more situations at any time. It’s thus much better to avoid the technique and work in a way that is supported.

(I’ve decided to change the terms I’m using from synchrony to atomicity. For users familiar with that domain: the problem is essentially equivalent to the scroll position being atomics that are changed from another thread, but you’re doing a load and a store, probably with relaxed sequential consistency, rather than using an atomic swap operation, or a mutex lock over a non-atomic. There is no recourse while using this technique because the scroll position is not exposed as an atomic or something lockable.)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: