Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

各キャラ紹介ページからボイボ寮に遷移した際に、該当キャラの位置までスクロールする #104

Open
k-chop opened this issue Oct 24, 2022 · 13 comments

Comments

@k-chop
Copy link
Contributor

k-chop commented Oct 24, 2022

Why

各キャラ紹介ページは #102 #103 によって独立したページになっているが、
元がモーダルダイアログだった関係で表示はモーダルっぽさを引き継いでいる。

各キャラ紹介ページからモーダルを閉じる感覚でボイボ寮一覧ページに遷移したときに
ページトップに飛ぶと違和感を覚えてしまうため直したい。

How

4e56c8a
該当キャラの位置へスクロールする処理自体は↑で実現可能。

NOTICE

  • 検索・共有からの直接流入個別ページ→一覧ページ
    • の場合のみ処理する。
  • 一覧ページ→個別ページ→一覧ページ
    • の場合は一覧ページのスクロール位置は動かさない(現状は navigate(-1) で戻る相当の処理で実現 )
    • 他のキャラを続けて見るときにスクロール位置がガタガタするとストレスフルなため
  • 検索・共有からの直接流入個別ページA→一覧ページ→個別ページB→一覧ページ
    • と遷移した際に個別ページAの位置に戻ってしまわないようにしたい
      • 上2つの実現方法との食い合わせでこれが達成できない

要件

@Hiroshiba
Copy link
Member

issue作成ありがとうございます!!

この実装が完了したらホームページの方にも反映して、告知させて頂きたいと思います・・・!

@Hiroshiba
Copy link
Member

さっき発想を教えてもらったのですが、/domitory/#zundamonというアンカー(?)を用意して、そこに飛ばすという手もあるかもです。
でも中央にスクロールする感じでは無いので、実装しちゃうのも全然良さそうに感じます。
参考になれば・・・!

@k-chop
Copy link
Contributor Author

k-chop commented Nov 1, 2022

失礼しました、反応遅くなりました。
優先度の確認をした上でissue分けたのはこの機能は必須ではない(=なくてもリリースできる)という意図だったのですが
伝わらずリリースをブロックさせてしまいすみません!!
(上に実装完了を待ってから反映すると書いてあり、それに対して+1リアクションもつけている・・・完全にぼけてました)

お待たせした上で大変申し訳ないのですが、期待した挙動が実現できなさそうでした!!

@k-chop
Copy link
Contributor Author

k-chop commented Nov 1, 2022

今回の個別ページ化において大事にしたいユーザ体験の優先度は

  • ① 寮生一覧ページと個別ページを行き来したときにスクロール位置を変えない
    • 続けて他のキャラを見たいときに先頭に戻ったりスクロール位置がガタガタするとストレス
  • ② 検索・共有された個別ページから寮生一覧ページへ遷移する時に、個別ページで表示していたキャラクターの位置までスクロールする
    • 理由はissue冒頭参照

①>>>>>②となっている認識です(そもそもここが合ってなかったらすみません)

①は個別→一覧の遷移をnavigate(-1)にすることで実現でき、
②は個別ページから寮生一覧ページへlocation.stateで表示していたキャラクター情報を受け渡すこと(またはアンカー)で実現できます。
色々試したのですが ①と②が両立できなかったので、2は諦めたいというのが今回の提案 です。

以下技術的な詳細

2を実現しようとすると、
「個別→(遷移A)→一覧→(遷移B)→個別→(遷移C)→一覧」と遷移する際に
遷移Aでlocation.stateに情報が乗るのですが、遷移Cで navigate(-1) すると 遷移Aを再現する挙動 になっており、乗っているlocation.stateも再現されます。
なので最初に表示した個別ページから飛んできた扱いになってしまいます。
例)ずんだもん→一覧→WhiteCul→一覧と遷移するとずんだもんの位置へ飛ぶ

これをアンカーにしても、
上記の例で言えば遷移Cで /dormitory#zundamon へ戻ろうとするので同じ問題が起きます。

navigate(-1) をやめて遷移Bでクリック時にスクロール位置を取得すればいけるのでは?と試しましたが
Linkタグを使っているのでクリック時のwindow.scrollYが取れない
→navigateでの遷移にするとクローラが子ページと認識できない問題再発しページ遷移にした意味がなくなる
という感じでした

@Hiroshiba
Copy link
Member

実装チャレンジと詳細なコメント、本当にありがとうございます・・・!

こちらでも2時間弱ほどいろいろ試してみたのですが、navigate(=ブラウザの戻る進む)とスクロール位置保存の挙動が想像と違っていてめちゃくちゃ難しいなと感じました。。。
ので、諦める方針に賛成です・・・。

以下いろいろなお答えと判断です。(長いです)

この機能は必須ではない(=なくてもリリースできる)という意図だった

なるほどです!
必須ではないというところは認識揃っていたのですが、完璧にしてからリリースしたいという意図なのかなと思っていました!
急いでリリースする予定もなかったのと、別のリリースと被っていたのでとりあえずお待ちさせて頂いた次第です 🙇

①と②が両立できなかったので、2は諦めたいというのが今回の提案

先程のとおり、賛成です。思った以上に難しい・・・。
賛成なうえで技術向上のためにいくつか議論できると嬉しいです。

navigate(-1) すると 遷移Aを再現する挙動
アンカーにしても
同じ問題

個人的にこの挙動がすごく不思議でいます。なんでスクロール位置が保存されないのでしょう・・・。
navigate(-1)location.stateは仕様かもなのですが、アンカーは他のサイトでも確認できるので色々試しました。

アンカーに遷移 → 手動で適当にスクロール → 別ページへ遷移 → ブラウザバック
したときの挙動ですが、例えばgithubだとボイボと同じくアンカーに戻る挙動でした。↓

VOICEVOX_voicevox_engine_.VOICEVOX.-.Google.Chrome.2022-11-02.04-24-19.mp4

でもよくある挙動は違っていて、例えばこちらのブログだとスクロール位置に戻りました。↓

a.click.-.-.Google.Chrome.2022-11-02.04-26-19.mp4

gatsbyのドキュメントQiita記事だと一瞬anchorに行ってからスクロール位置に行く感じでした。↓

Gatsby.Link.API._.Gatsby.-.Google.Chrome.2022-11-02.04-23-01.mp4

もしこの違いについてご存知だったら知りたいなと。。
ページのレンダリングのスピードによって変わってくるとかな気がしますが、navigate(-1)は一瞬なのでとても不思議でいます。
DOMがjavascriptで動的にレンダリングされてるかどうか・・・?

@Hiroshiba
Copy link
Member

なんで以前の #102 ではスクロール位置が保存されてたのか不思議なのですが、navigateではなくhistory.replaceを使っていたからでしょうか。

であれば、もし実現するとしたら、個別ページに来た場合に1回だけそのキャラにスクロール、あとはreplaceで見かけ上URLを遷移させるのが良いのかなと思いました。

という所感です。
いやあ・・・めちゃくちゃ難しいですね・・・。

@k-chop
Copy link
Contributor Author

k-chop commented Nov 3, 2022

完璧にしてからリリースしたいという意図

なるほど・・・!完全に言葉足らずでした、失礼しました 🙇

個人的にこの挙動がすごく不思議でいます。なんでスクロール位置が保存されないのでしょう・・・。

前提として、gatsbyのLinkコンポーネントによる同一サイト内遷移は、内部ではreach-routerを利用した遷移になっています。
なのでまず同サイト内リンクと外部リンクで挙動が違いますね。

あとスクロール位置を復帰する挙動は、ブラウザ側に実装されているものと、jsの制御で実現しているものがあります。
ref: https://zenn.dev/akfm/articles/recoi-sync-next

アンカーに戻るかスクロール位置に戻るかは、上記2つのどっちが働くかによるのだと思います。

たとえばgatsbyのドキュメントで アンカーセット → 同ドキュメントの別ページへ飛ぶ → 戻る
を行った場合はスクロール位置が保存されず、アンカーに戻ることを確認しました。
gastbyにもscroll restrationの仕組みは入ってるはずなので、アンカーがある場合はスクロール位置よりもアンカーの方を優先するとかなんですかね・・・

貼っていただいた例のように外部ページから戻ってくるパターンではブラウザ側のスクロール位置保存が効いていそうです。
アンカーに戻ったあとにスクロール位置へ戻るパターンはおそらくjs側→ブラウザ側の順で復帰が効いているのではないかと・・・(要出典)

@k-chop
Copy link
Contributor Author

k-chop commented Nov 3, 2022

#102 だと、個別ページは「最初から該当キャラのモーダルを開いている状態のボイボ寮ページをレンダリングしている」だけなので、

個別ページに来た場合に1回だけそのキャラにスクロール、あとはreplaceで見かけ上URLを遷移させる

ほぼこの状態になっていました。ページ遷移は発生せずモーダルの開閉だけなのでスクロール位置は変わりません。
ただリロードしたり別ページへ遷移して戻った場合は、スクロール位置は保存されず最初に開いていた個別キャラの位置に戻ってしまいます。

現状はボイボ寮ページと個別ページは完全に別のページになっています。
ページ間の親子関係をクローラが認識してくれない可能性がある、とのことでaタグでの別ページ遷移にしたためです。
この要件とOGP対応があるため、原神方式だとうまくいかないんですね・・・ 😭

@Hiroshiba
Copy link
Member

gastbyにもscroll restrationの仕組みは入ってるはずなので、アンカーがある場合はスクロール位置よりもアンカーの方を優先するとかなんですかね・・・

おー、ありがとうございます!!
どうしてもスクロールの挙動をいい感じにしたい場合は、このuseScrollRestorationとかを使うと良いのかもですね。
奥が深い・・・。

この要件とOGP対応があるため、原神方式だとうまくいかない

aタグ(Link)を用いつつ、replaceで遷移すれば良い感じ、という理解で合ってそうでしょうか👀
であれば、 #102 の状態から、replace=trueを指定したLinkで遷移すればいいのかな?と思った次第です!!

例えば 822a0e2#102 が入ったタイミングのコミットなのですが、その時のコードのこちら

<DormitoryCharacterCard
characterInfo={characterInfos.WhiteCUL}
onClick={() => showCharacterModal("WhiteCUL")}
/>

replace=trueなLinkで囲み、↓showCharacterModalの実装から
const showCharacterModal = (characterKey: CharacterKey) => {
const characterId = characterInfos[characterKey]?.id
window.history.replaceState({}, "", `/dormitory/${characterId}`)
setSelectedCharacterKey(characterKey)
setShowingCharacterModal(true)
}

history.replaceStateをなくす、みたいな感じです。

(なにか勘違いをしているかもしれません・・・ 🙇‍♂️ )

@k-chop
Copy link
Contributor Author

k-chop commented Nov 4, 2022

aタグ(Link)を用いつつ、replaceで遷移すれば良い感じ、という理解で合ってそうでしょうか👀

Linkタグのreplaceは今いるページの履歴を上書きして別のページに遷移する、という動作です。
ref: https://reach.tech/router/api/Link

history.replaceState とはちょっと違いますね(こちらは履歴とURLの上書きのみで別ページへの遷移が起きない)

history.replaceState(stateObj, '', 'bar2.html');
これにより、URL バーに https://www.mozilla.org/bar2.html が表示されるようになりますが、ブラウザーが bar2.html を読み込んだり、bar2.html が存在するかどうかを確認したりすることはありません。

ref: https://developer.mozilla.org/ja/docs/Web/API/History/replaceState

replace=trueを指定してもページ遷移は発生するので、そうした場合はそもそもshowCharacterModalは実行されなくなります。 使わないコードが残った #103 と同じになる感じですね。
さらに、replaceをつけた場合 /dormitory にいた履歴が上書きされて個別ページに行くという動作になるので
navigate(-1) で戻れなくなり、スクロール位置を保持して一覧ページに戻る方法がなくなります。
特に問題は解決しないですね・・・ 🙇

@Hiroshiba
Copy link
Member

あ、なるほど!!
Link replace=truehistory.replaceは同じ挙動をすると勘違いしていました 🙇‍♂️

詳細にありがとうございます!!
リンクとhistory.replace(スクロール周り)は排反でどちらか一方(いろいろ努力すればまあ解決できるかも)、ということがよくわかりました。
その2つならやはりリンクを優先すべきだと思うので、その実装ができている現在のmainブランチをリリースしたいと思います!!

@k-chop
Copy link
Contributor Author

k-chop commented Nov 4, 2022

ありがとうございました!! 🙏 (&やりとりがだいぶ長くなってしまいすみません)
中の議論を踏まえてissueの文章を再整理しました。
私は諦めますが他の誰かが颯爽と解決してくれるかもしれないことを祈って・・・

@Hiroshiba
Copy link
Member

ブラウザバックするとスクロール位置を無視してハッシュ箇所に戻ってしまう件についてもうちょっと調べてみました。

ここで報告されていて、closeされたあともなんか再発したっぽいんですが放置されてそうに思います。

どうやらhash urlに移動したときにそこに移動しない?みたいなバグがreach-routerにあるらしい?

でこれを薄めるために、hash箇所にスクロールするのがgatsbyに入ったっぽい?

でもこれはブラウザバック時にバグるのは残るので、そのままっぽい感じでした。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants