document.write()を使わない方法

document.write()は使わない」は、言われ続けて久しいと思いますが、じゃあどうすればいいか考えてみました。


document.write() がダメな理由

MDN document.write()説明ページにはどんなことが書かれているかというと、


deferred または asynchronous のスクリプト内では無視


→使い勝手が悪い


Chrome55からキャッシュされていないHTTP の場合、document.write()を通じて挿入された<script>要素は実行されない


→そもそも期待通りに処理が動かない可能性がある

など動作が特殊で、期待通り動かないことがあるということ強調されています。

さらにW3Cはdocument.writeのHTML5での使用を非推奨としていて、

HTMLパーサーに影響を与えるよーとか、実行しても無視されるか例外が投げられることがあるよーとか、ネットワークの待ち時間に依存するよーなどなど書いてありました。

やっぱり使うのはダメそうです。


document.write()に代わるHTML挿入方法


Element.insertAdjacentHTML

この記事を書く時に、私自身初めて知った挿入方法です。MDNのページはこちら

古くからある?一般的な?と言って正しいか悩みますが、innerHTMLを高機能にしたようなものと感じました。

innerHTMLは、

Element.innerHTML = '<p>hello!</p>';

というイコールを使ってコンテンツの挿入を行いますが、これは

Element.insertAdjacentHTML('beforebegin', '<p>hello!</p>');

のように第1引数にElementからの相対位置、第2引数に挿入するコンテンツを指定します。

innerHTMLではElement内のコンテンツをまるっと書き換えるだけでしたが、insertAdjacentHTMLでは細かな挿入位置の指定ができるのが魅力です。ほかにも Element.insertAdjacentHTMLやElement.insertAdjacentTEXTもあるので、用途に合わせて使えそうです。

もうinnerHTMLいらないかも・・。


appendChild & insertBefore

appendChild はノードの一番下にノードを追加できます。

// Elementの子ノードリストの一番下に追加する

const inserted_child = Element.appendChild(child);

insertBeforeは指定した要素の前に、ノードを追加できます。

// Elementの子ノード<h2>の直前に追加する

const inserted_child = Element.insertBefore(child, Element.querySelector('h2'));

nextSiblingを使って直後に追加もできます。

// Elementの子ノード<h2>の直後に追加する

const inserted_child = Element.insertBefore(child, Element.querySelector('h2').nextSibling);


これにて解決、解決。じゃない。

ちょっと待てと。

<body>内で <script>document.write()</script> をしていたときは、

document.write() 実行した場所にコンテンツが挿入されていたじゃないか!

上記の例であった、Elementが取得する必要があるけどそのやり方には全く触れてないじゃないか!

ということで、番外編:scriptが実行された位置の取得もご興味ある方はご覧ください。


解決方法その1:document.currentScript

<div>

<script>
const current = document.currentScript;
const a = document.createElement('a')
current.parentNode.appendChild(a);
</script>
<!-- ここに<a>が挿入される -->
</div>


解決方法その2:document.scripts[document.scripts.length-n]

currentScriptが取れない場合です。IE11ではcurrentScriptが利用できません。

<div>

<script>
const current = document.scripts[document.scripts.length-n];
const a = document.createElement('a')
current.parentNode.appendChild(a);
</script>
<!-- ここに<a>が挿入される -->
</div>

document.scripts.length-n-n は減算です。nは数字になり、少なくともdocument.scriptsはプロパティlengthを持っているのでその分-1する必要があります。

が・・この方法はうまくいかないかもしれない・・ちょっと検証ができたらまた更新します。


これにて解決、解決。かな・・?

もっといい方法があるかもしれない・・・。

コメントでアドバイスいただけたら嬉しいです。