😵💫
【React】動的にscriptタグを追加しても広告が表示されない理由と解決策
これは自身の 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>
試したこと(解決できなかった)
上記などを参考にしながら以下のようなコードを書いたがうまく表示されませんでした。
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" }} />;
}
原因
原因は、外部 JavaScript(上記の例では https://example.js
) に document.write
が含まれていると、React 環境で遅延ロードしても適切に動作しないことがあるためです。
また、useEffect
内で <script>
タグを動的に追加する方法では、document.write
は非同期にロードされたスクリプト内では無視される仕様となっています。
参考: Document: write() メソッド - Web API | MDN
解決方法
上記の Qiita の記事では postscribe を使用して解決しています。
postscribe は、document.write
や innerHTML
を使用して、ブラウザがネイティブにコンテンツを描画するのと同じ方法でコンテンツを挿入することを保証してくれます。これにより、複雑なパースやハックを行うことなく、ブラウザと同じように動作させることができます。
ただし、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