外部サイトからの遷移でURLにページ内リンクが入っていた時...
つまりURLが「/blog/2022/#見出し」のようになっていたとき
この時に対象の見出しまで画面がスクロールしないので困っていた。
私のケースではページ内の目次から正しく目的の見出しまでスクロールできるので、HTMLタグそのもの自体は正しいことがわかっている。
ではなぜ外部サイトから移ってきたときだけ遷移しないのだろうか。
今回の場合は遷移直後に「ロード画面」が用意されていたのが問題。
このロード画面は非常に僅かな時間でスマホ判定をしているだけなのだが、既にそのタイミングでページ内遷移判定が行われてしまうようだ。
したがってまだ見出しが描画されていない状態でスクロールするかどうかが判定されるので、#付きの見出しが存在せず画面は一番上のままになってしまう。
というわけでロードが終わってからjs側でページ内遷移を行えばよい。
これがとても簡単で
window.location.hash = "#遷移したいハッシュ"
のコードを実行してやれば画面が対象の見出しまでスクロールする。
また遷移してきた瞬間にwindow.location.hash
の中に#以降のハッシュが格納されているので
const test = window.location.hash
のようなコードでURL中の#指定したハッシュ値
を取り出すことができる。
ただ、このコードを正しいタイミングで実行しても画面遷移が発生しないケースがある。
window.location.hash
にもともと入っている値と全く同じ値を入れてもスクロールが作動しないという仕組みがあるからだ。
例えば外部から#test
というハッシュで遷移してきた場合、適当なタイミングでwindow.location.hash = "#test"
を呼び出しても画面は遷移しない。
前回と全く同じハッシュにスクロールしたい場合は一度クリアしてから再設定してやると動作する
つまり
const hashSave = window.location.hash
window.location.hash = ""
window.location.hash = hashSave
こんなコードを書いてやれば画面は再び対象のアンカーへスクロールする。
描画が終わった後(アンカー付きの見出しが書き出された後)にこの処理が走れば良いのでuseEffect()
を使えばよい。
よって最終的なコードは
React.useEffect(() => {
if (window.location.hash && 最初のロード終了フラグ) {
const hashSave = window.location.hash
window.location.hash = ""
window.location.hash = hashSave
}
},[
//ここにも最初のロード終了のフラグ
])
これで外部サイトから遷移してきた後、画面が描画されるのを待ってからページ内遷移の処理がもう一度走るはずだ。
一応解決には至ったが、このコードの書き方が冗長というか昨今のプログラムっぽくない(あまりスマートなコードではない)ので実は別の方法があるかもしれない。
また発見があれば更新する。