demo · v130

Gallery Scroll Sync

Click a thumbnail and both scroll containers animate concurrently: the thumbnail strip scrolls to center the active thumb, and the main view scrolls to the full-size image. Chrome 130 lets both scrollIntoView({behavior: 'smooth'}) calls run simultaneously — neither cancels the other.

Mode:
Pre-130 simulation: a 20ms delay between calls causes the first smooth scroll to be interrupted by the second.
Click a thumbnail to begin.

independent containers

not run yet

Click a thumbnail to measure the main scroller and thumbnail strip moving in the same timed window.

same-container rapid click

not run yet

Runs two thumbnail selections 160ms apart to show that a later call still aborts an earlier smooth scroll in the same scroll box.

the code

In Chrome 130 mode, both calls are issued in the same synchronous block — the browser animates them at the same time:

// Chrome 130: both scroll simultaneously — no cancellation.
thumbEl.scrollIntoView({ behavior: 'smooth', inline: 'center' });
mainEl.scrollIntoView({ behavior: 'smooth', block: 'start' });

Pre-130 simulation: a setTimeout delay means the second call starts while the first is animating, causing interruption:

// Pre-130: the second call fires while the first animation is mid-flight.
thumbEl.scrollIntoView({ behavior: 'smooth', inline: 'center' });
setTimeout(() => {
  // In pre-130 browsers this cancels the in-progress thumb strip animation.
  mainEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
}, 20);

Rapid-click boundary: two calls targeting the same scroll container still cancel the earlier animation; the newest thumbnail wins.

firstTarget.scrollIntoView({ behavior: 'smooth', block: 'start' });
await new Promise((resolve) => setTimeout(resolve, 160));
secondTarget.scrollIntoView({ behavior: 'smooth', block: 'start' });

see also