0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

仮想スクロールを入れたのに、画面が跳ねる。見落としていたのは「行の高さ」だった

0
Posted at

DOMを減らした。スクロールも軽い。でも、画像が読み込まれた瞬間に読んでいた場所を見失う。

仮想スクロールには、描画量を減らす仕事に加えて、各行の位置を正しく扱う仕事があります。行の高さが後から変わると、画面に出していない行まで座標に影響します。

GitHubが9月23日に公開した大規模PR表示の解説でも、この問題が登場しました。同社が扱った例は2,200ファイル、100万行超の変更、400件超のインラインコメント。難所のひとつは、描画するまで高さが決まらないコメントだったと説明しています。GitHubの技術解説

伸縮する舞台装置と、一部分を切り取る黄色い枠を描いたイラスト

AI生成の概念イラスト。内容の高さが変わっても、読み手が見ている場所を保つという課題を表しています。実装図ではありません。

表示していない行にも、高さが要る

たとえば、上から1万行あるリストで、画面周辺の50行だけをDOMに置くとします。これは説明用の数値です。

残りの行を描画しなくても、スクロールバーの長さや、5,000行目の位置は決めなければいけません。

すべての行が20pxなら簡単です。0始まりの行番号を i とすると、上端の座標は 20 × i。

途中に高さの変わるコメントが入ると、座標はこうなります。

行 i の上端 = 行 0 から行 i−1 までの高さの合計

コメントの折り返し、返信欄の展開、画像の読み込み。そのたびに高さの合計が変わります。

描画していないから、計算しなくていいわけではありません。

180px増えたら、読んでいた行も180px動く

以下はGitHubの実測値ではなく、仕組みを説明するための例です。縦方向だけを考え、スクロール範囲の端にはいないものとします。

読んでいる行の上端が文書内の2,000pxにあり、スクロール量が1,900pxなら、画面内では上から100pxの位置に見えます。

画面内の位置 = 文書内の位置 − スクロール量
100 = 2,000 − 1,900

その行より上のコメントが180px伸びると、文書内の位置は2,180pxになります。スクロール量を変えなければ、画面内の位置は280px。読み手には、行が下へ逃げたように見えます。

同じ100pxの位置に保つなら、スクロール量も180px増やします。

補正後のスクロール量 = 1,900 + 180 = 2,080
画面内の位置         = 2,180 − 2,080 = 100

この基準にする要素がアンカーです。W3CのScroll Anchoring仕様草案も、アンカーの移動に応じてスクロール位置を調整する考え方を定めています。仕様草案

下の行が伸びただけなら、上にあるアンカーの座標は変わりません。総高さが変わるたびに無条件でスクロール量を増やす実装では、逆に画面を動かしてしまいます。

ResizeObserverを付けたら完成、ではない

要素のサイズ変化を受け取る手段として ResizeObserver があります。ただし、これは変化の通知です。仮想リストの高さ情報やスクロール位置を、代わりに管理してくれるAPIではありません。MDN

実装を確認するときは、次の流れを追います。

  1. 変更前に、どの項目のどの位置を画面内に保つか決める。
  2. サイズ変更を高さ情報へ反映する。
  3. 同じ項目の新しい座標を求め、必要な分だけスクロール量を補正する。

ここで保存したいのは、単なる「上から何行目」だけではありません。先頭に項目が追加されても同じ内容を追えるように、安定した項目IDが必要です。

ブラウザー側のスクロールアンカリングと、ライブラリ側の補正がどう分担されているかも確認します。独自補正を足す前に、既存の仕組みで何が補正されているかを把握するのが先です。

テストデータは、長くするより途中で変える

固定長の1万行が滑らかでも、可変長コンテンツの確認は終わっていません。

次はリストの途中まで移動してから、読んでいる項目より上の画像を遅れて表示してみてください。続けて、ウィンドウを狭くして文章を折り返す。返信欄を開閉する。

見るのはフレームレートに加えて、同じ項目の同じ場所が、画面内に残っているか です。

項目自体の削除など、同じ場所を維持できない操作には別の方針が要ります。まずは「上の項目だけが伸びる」ケースで、意図しない移動がないかを確かめると原因を絞れます。

仮想スクロールのレビューで、ひとつ聞いてみてください。

このリスト、読み始めた後で高さが変わっても大丈夫ですか?

参考資料

GitHub:Rendering huge pull requests in the GitHub Copilot app(2026年9月23日)
https://github.blog/engineering/user-experience/rendering-huge-pull-requests-in-the-github-copilot-app/

W3C:CSS Scroll Anchoring Module Level 1(Working Draft)
https://www.w3.org/TR/css-scroll-anchoring-1/

MDN:ResizeObserver
https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver

資料確認日:2026年9月25日。数値例の計算を確認済みです。GitHubの表示性能を独自に測定したものではありません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?