Html5 window.history
APIを使用すると、Webアプリでナビゲーションをかなりうまく制御できます。
現在、アプリにはselectDate
(1)とenterDetails
(2)の2つの状態があります。
アプリがロードされると、私はreplaceState
とpopState
リスナーを設定します:
history.replaceState({stage:"selectDate",...},...);
window.onpopstate = function(event) {
that.toStage(event.state.stage);
};
日付が選択され、アプリがステージ2に移動すると、状態2がスタックにプッシュされます。
history.pushState({stage:"enterDetails",...},...);
この状態は、詳細が変更されるたびに置き換えられるため、履歴に保存されます。
ステージ2を終了するには、3つの方法があります。
戻るボタンは、popstate
リスナーによって処理されます。キャンセルボタンプッシュステージ1により、ユーザーが戻るボタンに入力した詳細に戻ることができます。これらは両方ともうまく機能します。
保存ボタンはステージ1に戻り、ユーザーが詳細ページに戻ることを許可しません(すでに送信されているため)。基本的に、yは履歴スタックをlength = 1にする必要があります。
しかし、history.delete()
またはhistory.merge()
が存在しないようです。私ができる最善の方法は、履歴スタックを["selectDate","selectDate"]
のままにするhistory.replaceState(stage1)
です。
1つのレイヤーを削除するにはどうすればよいですか?
編集:
他のことを考えましたが、それも機能しません。
history.back(); //moves history to the correct position
location.href = "#foo"; // successfully removes ability to go 'forward',
// but also adds another layer to the history stack
これにより、履歴スタックは["selectDate","selectDate#foo"]
のままになります。
それで、代替として、新しい状態をプッシュせずに「フォワード」履歴を削除する方法はありますか?
あなたはもう移動したかもしれませんが、...私が知る限り、履歴エントリ(または状態)を削除する方法はありません。
私が検討してきた1つのオプションは、JavaScriptで履歴を自分で処理し、window.history
オブジェクトを一種のキャリアとして使用することです。
基本的に、ページが最初にロードされるときにカスタム履歴オブジェクトを作成し(ここでは配列を使用しますが、状況に応じて適切なものを使用します)、最初のpushState
を実行します。カスタム履歴オブジェクトを状態オブジェクトとして渡します。これは、ユーザーがアプリから移動して後で戻ってくるのを処理する必要がある場合に役立つ可能性があるためです。
var myHistory = [];
function pageLoad() {
window.history.pushState(myHistory, "<name>", "<url>");
//Load page data.
}
ここで、ナビゲートするときに、独自の履歴オブジェクトに追加し(または、しないでください-履歴が手元にあります!)、replaceState
を使用して、ブラウザをループから外します。
function nav_to_details() {
myHistory.Push("page_im_on_now");
window.history.replaceState(myHistory, "<name>", "<url>");
//Load page data.
}
ユーザーが逆方向にナビゲートすると、「ベース」状態になり(状態オブジェクトはnullになります)、カスタム履歴オブジェクトに従ってナビゲーションを処理できます。その後、別のpushStateを実行します。
function on_popState() {
// Note that some browsers fire popState on initial load,
// so you should check your state object and handle things accordingly.
// (I did not do that in these examples!)
if (myHistory.length > 0) {
var pg = myHistory.pop();
window.history.pushState(myHistory, "<name>", "<url>");
//Load page data for "pg".
} else {
//No "history" - let them exit or keep them in the app.
}
}
ユーザーは常に最新のページにいるため、ブラウザボタンを使用して前方に移動することはできません。
ブラウザの観点からは、「戻る」たびに、すぐに再び前に押し出されます。
ユーザーの観点からは、ページを後方に移動することはできますが、前方には移動できません(基本的には、スマートフォンの「ページスタック」モデルをシミュレートします)。
開発者の観点からは、ユーザーがアプリケーションでナビゲートする方法を高度に制御しながら、ブラウザーで使い慣れたナビゲーションボタンを使用できるようにします。必要に応じて、履歴チェーンのどこからでもアイテムを追加/削除できます。履歴配列でオブジェクトを使用する場合、ページに関する追加情報(フィールドコンテンツやその他)を追跡することもできます。
ユーザーが開始するナビゲーションを処理する必要がある場合(ユーザーがハッシュベースのナビゲーションスキームでURLを変更するなど)、次のようなわずかに異なるアプローチを使用できます。
var myHistory = [];
function pageLoad() {
// When the user first hits your page...
// Check the state to see what's going on.
if (window.history.state === null) {
// If the state is null, this is a NEW navigation,
// the user has navigated to your page directly (not using back/forward).
// First we establish a "back" page to catch backward navigation.
window.history.replaceState(
{ isBackPage: true },
"<back>",
"<back>"
);
// Then Push an "app" page on top of that - this is where the user will sit.
// (As browsers vary, it might be safer to put this in a short setTimeout).
window.history.pushState(
{ isBackPage: false },
"<name>",
"<url>"
);
// We also need to start our history tracking.
myHistory.Push("<whatever>");
return;
}
// If the state is NOT null, then the user is returning to our app via history navigation.
// (Load up the page based on the last entry of myHistory here)
if (window.history.state.isBackPage) {
// If the user came into our app via the back page,
// you can either Push them forward one more step or just use pushState as above.
window.history.go(1);
// or window.history.pushState({ isBackPage: false }, "<name>", "<url>");
}
setTimeout(function() {
// Add our popstate event listener - doing it here should remove
// the issue of dealing with the browser firing it on initial page load.
window.addEventListener("popstate", on_popstate);
}, 100);
}
function on_popstate(e) {
if (e.state === null) {
// If there's no state at all, then the user must have navigated to a new hash.
// <Look at what they've done, maybe by reading the hash from the URL>
// <Change/load the new page and Push it onto the myHistory stack>
// <Alternatively, ignore their navigation attempt by NOT loading anything new or adding to myHistory>
// Undo what they've done (as far as navigation) by kicking them backwards to the "app" page
window.history.go(-1);
// Optionally, you can throw another replaceState in here, e.g. if you want to change the visible URL.
// This would also prevent them from using the "forward" button to return to the new hash.
window.history.replaceState(
{ isBackPage: false },
"<new name>",
"<new url>"
);
} else {
if (e.state.isBackPage) {
// If there is state and it's the 'back' page...
if (myHistory.length > 0) {
// Pull/load the page from our custom history...
var pg = myHistory.pop();
// <load/render/whatever>
// And Push them to our "app" page again
window.history.pushState(
{ isBackPage: false },
"<name>",
"<url>"
);
} else {
// No more history - let them exit or keep them in the app.
}
}
// Implied 'else' here - if there is state and it's NOT the 'back' page
// then we can ignore it since we're already on the page we want.
// (This is the case when we Push the user back with window.history.go(-1) above)
}
}
過去の履歴を削除したり読んだりする方法はありません。
自分の記憶で履歴をエミュレートし、history.pushState
ウィンドウpopstate
イベントが発生するたびに呼び出して試してみることができます(これは 現在受け入れられているMikeの答え によって提案されます)。動的Webアプリでブラウザーの履歴をまったくサポートしないよりもさらに悪いUXをもたらす多くの欠点があります。
そのため、仮想履歴を構築してそれを回避しようとしても、空白の履歴状態(戻る/進むには何も行われない)や、戻る/進むが一部をスキップする状況につながる可能性が非常に高いですあなたの歴史の完全な状態。