今回作ったもの
document.writeの恐怖
SPA(SinglePageApplication)においてJavaScriptのコードの中で天敵が存在する。それはdocument.writeだ。SPAはロード済みのページに対して、各箇所の内容を書き換えていくという動きをする。ノードの末尾にHTMLを強制追加するdocument.writeを実行されると、困ったとしか言い様がない状態となる。
これに対抗する術を考えてみた。そもそもdocument.writeが自分の希望する位置にデータを挿入してくれれば、全ての問題が解決するのだ。ならばそうしよう。
広告コード
document.writeを放り込んでくる相手として、楽天やAmazonの広告プログラムがある。そこで生成されたコードは、ものの見事にdocument.writeを呼び出しており、ページがロードされる段階でコードがその場所に無ければまともに動作しないのだ。
しかしSPAの普及を望むものとしては、
広告が貼れないからSPAなんて使えないじゃん!
とかいう流れでSPAの普及が妨げられては困るのだ。
さくっと解決
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>SPA広告テスト</title>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded",onLoad);
function onLoad(){
var button = document.querySelector("button")
button.addEventListener('click',function(){
var node = document.querySelector("div") //広告挿入先
var code = document.querySelector("textarea").value //広告コード
outputAd(node,code);
})
}
//outputAd(広告を挿入するノード,広告コード)
function outputAd(node,code){
//document.writeのフック
var documentWrite = document.write;
document.write = function(value){
node.innerHTML = value;
document.write = documentWrite;
}
//ノード内のデータを削除
while(node.childNodes.length)
node.removeChild(node.childNodes[0])
//広告コードをダミーノードに設定
var dummy = document.createElement('div');
dummy.innerHTML = code;
while(dummy.childNodes.length){
var child = dummy.childNodes[0];
dummy.removeChild(child);
if(child.nodeName === 'SCRIPT'){
//SCRIPTタグなら再生成
var script = document.createElement('SCRIPT');
if(child.src)
script.src = child.src;
script.innerHTML = child.innerHTML;
node.appendChild(script);
}
else
node.appendChild(child);
}
}
</script>
</head>
<body>
<textarea rows='10' cols="80">広告コードを挿入</textarea><br>
<button>広告の表示</button>
<div style='border:solid'>
ここに広告を表示
</div>
</body>
</html>
実行はhttpかhttps環境下で行わないと正常に動作しない。また、AdSenseのコードを貼り付ける場合は、対象のドメイン上で動かす必要がある。
動作原理として、まずは広告コードを実行するところから始まる。ただしinnerHTMLでそのままSCRIPTタグが混ざったコードを差し込んでも、HTMLの仕様上動作しないため、SCRIPTタグを作り直すという処理が事前に必要になる。そして広告コード実行後、document.writeによるHTMLの追加が起こるので、それをフックして対象ノードへ転送する。
今回のコードはAmazon、楽天、AdSenseで動作を確認している。ちなみにAdSenseはdocument.writeを使用しないが、今回のコードで動作する。
しかしこのコードには致命的な欠点がある。同時に複数の広告の設定を行うと、document.writeのデータの転送先が特定できなくなってしまうのだ。この件でもし何か良い解決方法があったら教えて欲しい。
実は
そもそもこんな回りくどいことをしなくても、IFRAMEを作ってそこへコードを放り込めば動作する。そして複数広告問題も発生しない。しかしそれでは負けた気がするので、今回のコードを作ってみた。
現在https://croud.jp/で広告のテストを行っているが、システムを作った時に広告のことなど考えずに作ったので、フロントエンド側のコードが酷いことになってしまった。ということで機能ごとにプラグインを組み込むようなやり方が出来るように作り直そうと思っている。