5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SPA(SinglePageApplication)の天敵document.writeに対抗する ~広告コードは永遠に~

Posted at

今回作ったもの

 SPAでページを切り替えず、広告を挿入する
2018.12.04-17.07_7.gif

document.writeの恐怖

 SPA(SinglePageApplication)においてJavaScriptのコードの中で天敵が存在する。それはdocument.writeだ。SPAはロード済みのページに対して、各箇所の内容を書き換えていくという動きをする。ノードの末尾にHTMLを強制追加するdocument.writeを実行されると、困ったとしか言い様がない状態となる。

 これに対抗する術を考えてみた。そもそもdocument.writeが自分の希望する位置にデータを挿入してくれれば、全ての問題が解決するのだ。ならばそうしよう。

広告コード

 document.writeを放り込んでくる相手として、楽天やAmazonの広告プログラムがある。そこで生成されたコードは、ものの見事にdocument.writeを呼び出しており、ページがロードされる段階でコードがその場所に無ければまともに動作しないのだ。
 しかしSPAの普及を望むものとしては、

 広告が貼れないからSPAなんて使えないじゃん!

 とかいう流れでSPAの普及が妨げられては困るのだ。

さくっと解決

test.html
<!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/で広告のテストを行っているが、システムを作った時に広告のことなど考えずに作ったので、フロントエンド側のコードが酷いことになってしまった。ということで機能ごとにプラグインを組み込むようなやり方が出来るように作り直そうと思っている。

5
3
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
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?