haltu / muuri

Infinite responsive, sortable, filterable and draggable layouts
https://muuri.dev
MIT License
10.77k stars 644 forks source link

setTimeout required to scroll item into view for onFinish of show #499

Open petejodo opened 2 years ago

petejodo commented 2 years ago

For the case where a grid item that is shown overflows and wanting to scroll it into view, it requires using setTimeout to get it working. This is fine I suppose but it feels like a workaround. Maybe there's a different way to do this that I'm missing though. Here's the snippet of code that requires the setTimeout

grid.show([item], {
  onFinish: () => {
    setTimeout(() => {
      item?.getElement()?.scrollIntoView();
    }, 0);
  },
});

I believe this occurs because layout actually occurs after onFinish is called but not exactly sure why that's causing the issue. Thanks!

niklasramo commented 2 years ago

@PeteJodo That's an interesting use case I hadn't considered myself before. In any case, reading from the code, onFinish callback of the show method is supposed to be called after the show process is finished. So basically the shown items should be fully shown when onFinish is called, but the layout is not guaranteed to be finished at that point yet.

One additional thing to keep in mind is that by default the layout process is async (as the layout is computed in web workers) and you can configure (via syncWithLayout option) if the show method should wait for the next layout process to start before starting the show process of the requested items. By default, syncWithLayout is true, and it only makes sure that the show process is started in sync with the layout process, but does not guarantee that it is ended in sync with the layout process.

I think you need to utilize layoutStart and layoutEnd events if you want to make sure that layout is finished before you call element.scrollIntoView. You could probably do something like this:

if (item?.isPositioning()) {
  const onLayoutEnd = () => {
    grid.off('layoutEnd', onLayoutEnd);
    item?.getElement()?.scrollIntoView();
  };
  grid.on('layoutEnd', onLayoutEnd);
} else {
  item?.getElement()?.scrollIntoView();
}
petejodo commented 2 years ago

Oh that's interesting. Yeah that makes sense, I'll experiment with that snippet of code and comment back on how it goes. Thanks for the tip