SMART LLC

戻るボタンを判定して入力内容を復元する方法

公開日:2014/10/30

入力フォーム→間違ってリンクをクリック→戻るボタン→入力フォームが空( ゚д゚)
入力フォーム→確認ページ→戻るボタン→「Webページの有効期限が切れています」( ゚д゚)
苦労して入力した内容が消えた時の絶望感。
セッションを使用したページの入力内容を戻るボタンで復元する方法をメモする。

戻るボタンとキャッシュ

上記の絶望感は入力フォームのあるページのキャッシュが無効になってる場合に味わえる。
キャッシュが無効のページに戻る場合、GETリクエストしたページなのかPOSTリクエストしたページなのかで動作が変わる。
GETリクエストしたページに戻る場合、新しくリクエストを送信するため入力フォームが消えた初期状態のページが表示される。
POSTリクエストしたページに戻る場合、新しいリクエストを送信しないため期限切れエラーが表示される。
どちらにしてもキャッシュを有効にしてやれば解決する。

セッションとキャッシュ

PHPの場合、セッションを使用するとデフォルトではキャッシュが無効になってしまう。
session_cache_limiter()関数を使えばキャッシュをコントロールできそう。
各リクエスト毎に呼べて書いてあるけど常にsession_start()関数の前で呼んでおけばいいのかな多分。
ほとんどの場合はこれで解決だと思う。

リクエストとキャッシュ

問題なのは戻るボタンでもリクエストを送信したい場合。
XSS対策で入力ページ→確認ページのルートもCSRFトークンを使ってるから困った。
一度確認ページに遷移した時点でそのトークンは使用済になって次の認証には使えない。
つまり確認ページで戻るボタンを押した場合、リクエストを送信して新しいトークンを生成する必要がある。
入力内容をキャッシュしたいけどリクエストは送信したい。
リクエストを送信したいけど入力内容はキャッシュしたい。
そんなわがまま機能はないのであった(:3_ヽ)_
仕方ないのでキャッシュは無効にして入力内容の復元ロジックを自作することにした。

Web Storage

まずは入力内容を記憶する場所。
同時に複数のタブで同じ入力ページを開く可能性を考慮すると入力内容をタブ毎に保持する必要がある。
記憶領域といえばクッキーとセッション変数が思いつくけどどっちもタブ毎じゃない。
見つけたのがWeb Storage(σ・∀・)σ
Web Storageはクライアント側の記憶領域でHTML5で新登場したらしい。
クッキーと違って不用意に送信されることもないという。
最大サイズは5MBとか。
Web StorageにもLocal StorageとSession Storageの2種類ある。
Local Storageは全体で共有なのに対してSession Storageはタブ単位。
Local Storageは消すまで消えないのに対してSession Storageはタブと運命共同体。
複数タブ対応かつタブと一緒に消えてくれるSession Storage最強説。
Java Scriptで操作する。オブジェクト名はsessionStorage。

確認ページのアンロード時にWeb Storageに入力内容を記憶する。

window.onunload = function(){
	sessionStorage.setItem('test',document.getElementById('test').value);
}

入力ページのロード時にWeb Storageの内容を復元して削除する。

window.onload = function(){
	document.getElementById('test').value = sessionStorage.getItem('test');
	sessionStorage.clear();
}

setItem()関数で書いてgetItem()関数で読んでclear()関数で削除。
削除することで入力ページで更新ボタンを押したりURLを打ち直すと初期状態になる。
戻る→戻る→進むでも復元したいなら削除しない。
確認ページの先のページ(DB登録ページや送信ページ)に遷移した場合は不要になるはずなので消してOK。
ただこのままだと確認ページから入力ページに戻るボタン以外の方法(リンクやURL直打ち)で遷移した場合も入力内容が復元されてしまう。
これを防ぐために戻るボタンでアクセスしたかどうか判断する必要がある。

History API

戻るボタン判定に使うのはHistory API。
ページを推移した履歴を修正したり追加したり戻ったり進んだりできる。
この履歴もタブ毎なので複数タブ対応もOK。
Java Scriptで操作する。オブジェクト名はhistory。
簡単に戻るボタン判定を実装できた。

入力ページのロード時。

window.onload = function(){
	if(history.state=='entry'){
		document.getElementById('test').value = sessionStorage.getItem('test');
	}
	sessionStorage.clear();
	history.replaceState('entry',null);
}

replaceState()関数で現在ページの履歴情報を書き換えてる。
stateプロパティに目印をつけて判定した。
新規にページをロードした場合=目印がついていない(null)=Web Storageを削除
履歴を戻ってロードした場合=目印がついてる('entry')=Web Storageの内容を復元して削除
戻るボタン→更新ボタンでも目印はついてるから正確には戻るボタン判定というより履歴か新規か判定?
これでやっと戻るボタン押下時のみリクエストを送信しつつ入力内容を復元するページが実現できましたとさ(:3_ヽ)_

目印作戦による戻るボタン判定はURLに#をつけた履歴を追加しても同じことができそうだけどダサいから却下。
今回の方法だと確認ページで記憶するから入力ページから間違って別のページに遷移したり前のページに戻る場合は入力内容は保持されない。
誤操作による入力内容の消失を諦めるなら問題なし。
入力ページのアンロード時に記憶すれば解決できるけど今度は入力ページに戻った後に更新ボタンを押してもURLを打ち直しても入力内容がリセットできなくなる。
遷移先のURLが同じだと履歴が追加されなくてまた同じ履歴にアクセスしてる。
どっちをとるか。
もうひとつ重要なことに気づいた。
Web Storage使わないでもHistory APIだけで実現できそう。
てか試したら実現できた。
またあとでメモしよう。

SHARE