【v130で修正済み】Chrome で scrollIntoView を同時に実行すると動かないバグがある

TL;DR

  • 2017年ごろから発生していた scrollIntoView を同時に複数実行すると 1つしか動作しないバグが存在する。*1
  • 次バージョン v130 でやっと修正される予定*2
  • それ以前のバージョンでサポートしたい場合は scrollTo を使うことで回避できる

scrollIntoView を同時に使う

scrollIntoView という便利なプロパティが存在します。対象の要素にスクロールするためのものでスムーススクロールをする際によく使われるものかと思います。

ですが、Chrome のみ scrollIntoView を同時に複数実行すると崩壊するバグが存在します。Safari や Firefox では問題なく同時実行できるので本当にバグです。また、手動でスクロールする際にも scrollIntoView が止まってしまうようです。

scrollIntoView({behavior:'smooth'}) cancelled by user scroll or another call in unrelated scrolling box [40823405] - Chromium の issue にいい例があるのでぜひ試してみてください。

左側をスクロールすると右側のスムーススクロールが止まってしまう

codepen.io

なんと v130 で修正される

以下の記事によると ウェブ版、Android版ともに v130 にて修正されていることが確認できます。実際、私がこの問題にハマったときは自分は Chrome Dev (v131) を使用していたため動作確認時に見逃してしまったということがありました。

chromestatus.com

v130 まで待てない場合

しかし、全員が Chrome を常に最新にしているわけではないのでバグが修正されてもしばらくはこの問題に対応しなければなりません。ありがたいことにこの問題は解決策が存在しており、以下の Stackoverflow で解説されています。

stackoverflow.com

scrollIntoView の代わりに scrollTo を使用することで解決できるようです。複数 scrollIntoView があればすべて scrollTo に変える必要があります。

変え方は簡単で、scrollTo は指定した X/Y 座標までスクロールするものなのでスクロールしたい要素の座標を計算して scrollTo に渡すことができます。

例えば、以下のようなコードは

targetElement.scrollIntoView({
  block: 'start',
  inline: 'nearest',
  behavior: 'smooth',
});

このように変換ができそうです。

const targetY = targetElement.getBoundingClientRect().top + window.scrollY;
window.scrollTo({
  top: targetY,
  behavior: 'smooth',
});

感想

Chrome だけ発生するバグはあまり踏んだことがなかったので結構面白かった。