😵‍💫

【React】動的にscriptタグを追加しても広告が表示されない理由と解決策

2025/02/19に公開

https://zenn.dev/link/comments/5f36855e267546

これは自身の Scrap に書いたものをまとめた記事です。

対象読者

  • React を使っている人
  • 外部 JavaScript を読み込んで広告などを表示させたいが useEffect などで動的に埋め込む方法ではうまくいかない人

背景

以下のような HTML を React で書いてページに広告を表示したい。

<script type="text/javascript">
  var hoge = {
    ad_id: "xxxxxx",
  };
</script>
<script type="text/javascript" src="https://example.js"></script>

試したこと(解決できなかった)

https://zenn.dev/goshouhiro/articles/react-ninja-admax
https://qiita.com/tacchan5424/items/4627d21a1bc7d3b17b05

上記などを参考にしながら以下のようなコードを書いたがうまく表示されませんでした。

const cleanup = () => {
  const scripts = document.querySelectorAll('script[data-ad="true"]');
  for (const script of scripts) {
    script.remove();
  }
};

export function Ad() {
  useEffect(() => {
    cleanup();

    const inlineScript = document.createElement("script");
    inlineScript.innerHTML = `
      var hoge = {        
        ad_id: "xxxxxx",                
      };
    `;
    inlineScript.setAttribute("data-ad", "true");
    document.body.appendChild(inlineScript);

    const externalScript = document.createElement("script");
    externalScript.src = "https://example.js";
    externalScript.setAttribute("data-ad", "true");
    document.body.appendChild(externalScript);

    return cleanup();
  }, []);

  return <div style={{ width: "100%", minHeight: "100px" }} />;
}

原因

https://qiita.com/kazu_death/items/c3a2397349cf63a8ea68

原因は、外部 JavaScript(上記の例では https://example.js) に document.write が含まれていると、React 環境で遅延ロードしても適切に動作しないことがあるためです。

また、useEffect 内で <script> タグを動的に追加する方法では、document.write は非同期にロードされたスクリプト内では無視される仕様となっています。

参考: Document: write() メソッド - Web API | MDN

解決方法

上記の Qiita の記事では postscribe を使用して解決しています。

postscribe は、document.writeinnerHTML を使用して、ブラウザがネイティブにコンテンツを描画するのと同じ方法でコンテンツを挿入することを保証してくれます。これにより、複雑なパースやハックを行うことなく、ブラウザと同じように動作させることができます。

ただし、postscribe は TypeScript に対応していないため、今回は @marshallku/react-postscribe を使用しました。

最終的に以下のようなコードになりました!

pnpm add @marshallku/react-postscribe
import PostScribe from "@marshallku/react-postscribe";
import { useEffect, useRef } from "react";

const cleanup = () => {
  const scripts = document.querySelectorAll('script[data-ad="true"]');
  for (const script of scripts) {
    script.remove();
  }
};

export function Ad() {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!ref.current) return;

    cleanup();

    const script = document.createElement("script");
    script.type = "text/javascript";
    script.setAttribute("data-ad", "true");
    script.textContent = `
      window.hoge = {        
        ad_id: "xxxxxx",                
      };
    `;
    document.body.appendChild(script);

    return cleanup();
  }, []);

  return (
    <div ref={ref} style={{ width: "250px", height: "250px" }}>
      <PostScribe html={`<script src="https://example.js"></script>`} />
    </div>
  );
}

Discussion