◆ history.pushState で移動してから戻ってきたときにスクロール位置は復元される
◆ history.scrollRestoration の設定で無効にすることも可能
◆ ページ全体以外の要素内スクロールは復元できない
  ◆ 通常のページ遷移でも bfcache がない Chrome だと復元できない

スクロール位置が復元されてる

前の記事で SPA にするかしないかを考えてるときに JavaScript でページを切り替えだと スクロールバーの位置がそのままになってしまう問題もあると思ってました
移動時は新規にページを読み込むのと一緒なので

window.scrollTo(0, 0)

でリセットで良いです

しかし 戻る・進むボタンを使うときは 移動直前の状態に戻ってきてほしいものです
間違ったリンクをクリックして移動した後に戻るボタンで戻ってきたらスクロール位置が一番上に戻ってるのは不便です

これまでにスクロール位置が復元されない SPA サイトを見たことが何度もあったので 復元されないものと考えていたのですが 試してみると復元されていました

試せるページを用意しました

DEMO

SPA みたいに常にデフォルトページを返すようにするのは面倒だったので History API で移動するのはクエリパラメータの中だけにしてます
URL 的には この 3 ページです

  • ?page=1
  • ?page=2
  • ?page=3

現在のスクロール位置がわかりやすいように 適当にスペースを空けて連番を表示してます
連番の横の数値は再レンダリングされてることを確認するためのランダムな数値です

右上のボタンで History API を使ったページ移動ができます
適当にスクロールしてから ボタンを押してページ移動して またスクロールしてから 戻る・進むボタンを押すと移動前のスクロール位置に戻って来れていることがわかります

ページの長さが異なっても問題ありません
page1 より page3 のほうがコンテンツ量が多くページが長いです
page3 で下の方まで移動してから page1 に移動します
そこから戻るボタンで戻っても page3 の下の方になっています

復元しない方法もあった

調べてみると復元させないという方法もありました
history.scrollRestoration というプロパティがあって auto か manual を設定できます
デフォルトが auto なので自動で復元されます
manual を入れると自動で復元されなくなります

history.scrollRestoration = "manual"

上で書いたリンクのページのクエリパラメータに scroll=manual を入れれば試せます
↓がクエリパラメータ付きのリンクです

DEMO

戻るボタンを押してもスクロール位置はそのままになっています

復元できないケース

復元してくれるのは便利だなと思ったのですが この機能ってページ全体のスクロールだけのような気がします
例えば

<header></header>
<main></main>
<footer></footer>

のような構造で main に 「overflow: auto」 を使ってスクロールさせるような場合もあります
flexbox や grid が使えるようになってからはこういうのが多くて ページ全体をスクロールさせる作りは減ってきてると思います

このときの main のスクロール位置まで復元できるのでしょうか
試せるページを用意しました

DEMO

ダメみたいですね
こういうケースでも復元させたいなら スクロール位置を自分で保持して画面描画後に移動させるしかなさそうです

普通のページ遷移でも

ただ この場合って普通のページ遷移だと復元されるのでしたっけ
そう思って試してみると復元されませんでした

ページバック後に完全にページが読み直されます
JavaScript を実行して main 要素などを作ってるわけですから 一番上に戻ってます

そういえば Chrome って bfcache まだなんでしたっけ
結構前のたしか Chrome 50 くらいのときに bfcache がないせいで input の情報が変に復元されて困るみたいなことがあって そのしばらく後に Chrome でも bfcache が導入されるみたいな話を聞いて とうとうあの現象も起きなくなるんだとか思ったはずなのですが 未だに bfcache が有効になっておらず 戻ったときには毎回 JavaScript が 1 から実行しなおされています
テスト導入して問題あったとかなんでしょうか

まとめ

ページ全体のスクロールは History API でも復元してくれて それ以外の要素内スクロールは History API を使う使わない関係なく復元されません
スクロールバーの復元に関しては SPA のデメリットは特になさそうです