🔥

iOSでwindow.addEventListenerのloadが発火しない

2024/02/27に公開

目的

スマートフォンでwindow.addEventListener("load", method);というコードが上手く動かなかったため、対策についてまとめます。

経緯

今回の実装では、ページの読み込み後にメソッドを走らせてほしいという指示が出ていました。しかしiOS端末で挙動を確認したところ、発火する時としない時がありました。

修正前

  const method = () => {
    console.log("ページの読み込みが完了しました。");
  };
// マウント時にメソッドの発火予約(loadしたらメソッドを走らせてね)
  useEffect(() => {
    window.addEventListener("load", method);
    return () => {
      window.removeEventListener("load", method);
    };
  }, []);

原因

そこで、こちらの記事を発見しました。

https://stackoverflow.com/questions/73432196/window-addeventlistenerload-works-fine-on-pc-but-not-on-mobile

理由は、モバイルからサイトをロードしたときにのみDomがロードされなかったにもかかわらず、ロードイベントがすでにサーバーで発生しているためだと思います。

window.addEventListener箇所で「loadのタイミングでメソッドを走らせてね」という設定をしていますが、この時既にloadが完了していたら発火のタイミングがありません。これが発火しない時がある原因でした。

解決

という事で、条件分岐を追加しました。

  useEffect(() => {
    // 既にページの読み込みが完了していたら、即発火
    if (document.readyState === "complete") {
      method();
    } else {
    // まだ読み込み完了していなかったら、loadのタイミングの発火を予約
      window.addEventListener("load", method);
      return () => {
        window.removeEventListener("load", method);
      };
    }
  }, []);

document.readyState

https://developer.mozilla.org/ja/docs/Web/API/Document/readyState

Document.readyState プロパティは document の読み込み状態を記述します。

また、このような記述がありました。
document.readyState === "complete"はloadイベントの代替になるという事が分かります。

readystatechange を load イベントの代替とする

// Alternative to load event
document.onreadystatechange = () => {
  if (document.readyState === "complete") {
    initApplication();
  }
};

ちなみに、今回の場合はuseEffectのマウント時に1度だけ発火させており、そのタイミングでcompleteになっていなければ発火されないので条件分岐で予約設定も記述する必要がありました。

まとめ

スマートフォン(主にiOS)でwindow.addEventListener("load", method);というコードが上手く動かなかった原因は、予約設定をした際に既にloadが完了しており、発火のタイミングを逃していたことでした。
そこで「loadが既に完了していたら即発火する」という条件分岐を追加し、無事に解決しました。

同じような状況が起こった際のお役に立てれば幸いです。
お読みいただきありがとうございました。

Discussion