LoginSignup
1
0

はじめに

svelteを使っていて、popstateの発火時に、$page.url.hrefwindow.location.hrefで取得される値が異なっていて困ったので調べてみました。

環境

以下のようなコードを用意します。

+page.svelte
<script lang="ts">
  import { page } from "$app/stores";

  let uid = 0;
  let state = {};

  function pushState() {
    uid += 1;
    history.pushState(uid, "", `/${uid}`);
  }

  function popState(event: PopStateEvent) {
    console.log($page.url.href, window.location.href);
    state = event.state;
  }
</script>

<svelte:window on:popstate={popState} />

<button on:click={pushState}>click me</button>

<pre>{JSON.stringify(state)}</pre>

このコードは以下URL内の改変です。
https://svelte.dev/repl/ecfaef5f5e5f02c2ebf160b4ce06ac0a?version=4.2.9

click meを押した後、この中のconsole.logの結果が異なります。

例えば、click meを2回押したとき、{{url}}/2になります。
その後、ブラウザの戻るボタンを押した時、$page.url.hrefの結果は{{url}}/2ですが、window.location.hrefの結果は{{url}}/1になります。

調べた結果

そもそも、popstateの発火時として、document はまだ反映されていないかもしれないことを考慮に入れておくことが重要ですとあります。

When writing functions that process popstate event it is important to take into account that properties like window.location will already reflect the state change (if it affected the current URL), but document might still not. If the goal is to catch the moment when the new document state is already fully in place, a zero-delay setTimeout() method call should be used to effectively put its inner callback function that does the processing at the end of the browser event loop: window.onpopstate = () => setTimeout(doSomeThing, 0);

popstate イベントを処理する関数を書くときには、 window.location のようなプロパティはすでに状態の変化を反映していますが(それが現在の URL に影響する場合)、 document はまだ反映されていないかもしれないことを考慮に入れておくことが重要です。新しい文書の状態が完全に反映された瞬間を捉えることが目的であれば、遅延ゼロの setTimeout() メソッド呼び出しを使用して、処理を行う内部の callback 関数をブラウザーのイベントループの最後に効果的に配置する必要があります。例えば window.onpopstate = () => setTimeout(doSomeThing, 0); のようにします。

ただ、今回の例であればconsole.logのタイミングでdocument.URLを見ても、window.location.hrefと同じ値が返ってくると思います。
また、その続きである新しい文書の状態が完全に反映された瞬間を捉えることが目的であれば ~~~の部分の通り、console.logの後にsetTimeout(() => console.log($page.url.href), 0);を入れてみると、window.location.hrefと同じ値になります。

ちなみに、$page.urlの変更タイミングは恐らくここになります。
https://github.com/sveltejs/kit/blob/99612ace885a6085b87e1ee1f38b0eea9ae2ba14/packages/kit/src/runtime/client/client.js#L2172-L2173
上の方を見ていくと、addEventListener('popstate'とあるため、popstateの発火タイミングのずれによって変更されてないため、値が取れないということが分かります(試しにlog埋め込んでみると分かります)

まとめ

どうも似たような質問も飛んでました。
https://www.reddit.com/r/sveltejs/comments/14frcs4/browsers_popstate_event_handling/

解決方法としてはsetTimeoutを使ってでも$app/storespageを用いるか、ここはいっそwindow.location.hrefの二択になると思います。
もし他に良案があれば教えてください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0