参考メモ/innerHTMLとdocument.writeのhookついて昔のメモ

1-2年ほど前にinnerHTMLとdocument.writeを自前のfunctionに差し替えられないか(=hook)とか、どういうコードで呼ばれるのか調べたメモ。

WebブラウザとJSの世界で1-2年ほど前となると、もう昔の話になる上、なぜそもそもそんなことを調べようとしたのかも忘れてしまったので、単なる参考メモです。

今回Qiitaにメモ整理する際に、メモに残ってたコード断片を動かしてみた検証ブラウザバージョン：

- Chrome 49
- Firefox 45
- IE 11
- Edge 25

# window, window.document

window がブラウザ上でJavaScriptを動かす時の、省略時のグローバルスコープ。

参考：

- Window - Web APIs | MDN
  - https://developer.mozilla.org/en-US/docs/Web/API/Window
- Window.document - Web APIs | MDN
  - https://developer.mozilla.org/en-US/docs/Web/API/Window/document
- HTML Standard -> document.open()
  - https://html.spec.whatwg.org/multipage/webappapis.html#dom-document-open

# document.write() はイベントハンドラでは使わない

イベントハンドラ中でdocument.write() を使うと新しいドキュメントを生成してしまうので、画面がクリアされてしまう：

- サイ本15章ドキュメントの制御 - tomoemonの日記
  - http://tomoemon.hateblo.jp/entry/20110714/p1

例えばWeb画面のレンダリングが終わった後に、開発者コンソールを開いて `document.write('hello');` など打ち込むと、検証環境のすべてのブラウザで画面が真っ白になり "hello" と表示される。

なんかメモに、「イベントハンドラからのフローでdocument.writeが出てくる懸念は無くなった。」と書いてるので、なんか、でてきたらまずいようなの作ろうとしてたのかも。

# document.write() を入れ替える

```
var oldw = document.write.bind(document);
document.write = function() { console.log("hello"); oldw("fuga"); }
document.write();
```

検証環境すべてで、上記3行を一回実行するだけに限定すれば、動くことは動く。ただし複数回呼び出した時にどうなるかや、他への副作用については不明。

# 自分でもなぜこのトピックを調べたのか忘れた、参考リンクメモ

多分、innerHTMLを独自関数でhookしようとしていろいろググってたっぽい。

- window.location - Web API インターフェイス | MDN
  - https://developer.mozilla.org/ja/docs/Web/API/Window/location
- element.innerHTML - Web API インターフェイス | MDN
  - https://developer.mozilla.org/ja/docs/Web/API/Element/innerHTML
- HTML DOM innerHTML Property
  - http://www.w3schools.com/jsref/prop_html_innerhtml.asp
- innerHTML property (Internet Explorer)
  - https://msdn.microsoft.com/library/ms533897(v=vs.85).aspx
- .html() | jQuery API Documentation
  - http://api.jquery.com/html/
- hasegawayosuke/rickdom: RickDOM is a javascript library to build DOM elements from string safety using DOMParser API or createHTMLDocument API of modern browsers.
  - https://github.com/hasegawayosuke/rickdom
- IE8 の DOM のプロトタイプと Getter/Setter API はどうなるか - IT戦記
  - http://d.hatena.ne.jp/amachang/20090115/1231989477
- Document Object Model Prototypes, Part 2
  - https://msdn.microsoft.com/en-us/library/dd229916(v=vs.85).aspx


# ChromeとinnerHTMLについてのメモ

メモしてた当時、ChromeのinnerHTMLについてちょっと特殊な状況があったらしい。

- Issue 13175 - chromium - `__lookupSetter__/__lookupGetter__` don't return native setters and getters - Monorail
  - https://bugs.chromium.org/p/chromium/issues/detail?id=13175
- Issue 43394 - chromium - DOM attributes should be moved to prototype chains and should expose JavaScript getters/setters - Monorail
  - https://bugs.chromium.org/p/chromium/issues/detail?id=43394

innerHTMLはElement.prototypeのプロパティではなくて、Elementオブジェクトのプロパティになってるのが、Chrome独自過ぎるのでやめよう、Element.prototypeに移そう、というので進行中（当時は）。

![20160327_innerHTML_chrome49.png](https://qiita-image-store.s3.amazonaws.com/0/18618/2847bf57-bc5d-e132-ce8d-ec853fd66618.png)

![20160327_innerHTML_ff45.png](https://qiita-image-store.s3.amazonaws.com/0/18618/bdfd796f-cdcc-0adf-87a6-89054a37879f.png)

![20160327_innerHTML_ie11.png](https://qiita-image-store.s3.amazonaws.com/0/18618/2c031ff1-866d-0203-fd08-b58aac145114.png)


→今はChromeでもElement.prototypeのプロパティになったのかな？IE系は謎。

# document.createElement()をhookすれば、innerHTMLから呼ばれないか？→呼ばれない。

`document.createElement()` をhookしてみる：

```
var ce = document.createElement;
document.createElement = function(tagname) { console.log("hooked"); return ce.call(document, tagname); };
var b = document.createElement("b");
b.innerHTML = "<i>abc</i>";
document.getElementsByTagName("body")[0].appendChild(b);
```

検証ブラウザすべてで、BタグをcreateElement()した時は "hooked" と表示された。しかし innerHTML プロパティにsetした時は "hooked" は表示されなかった。

おそらく innerHTML はnativeコードを参照しているため、hookされたcreateElement()は呼び出していない。

関連参考リンク：

- javascript - `__defineSetter__` on innerHTML stops it from rendering - Stack Overflow
  - http://stackoverflow.com/questions/2920180/definesetter-on-innerhtml-stops-it-from-rendering
- Chrome support for innerHTML - Google プロダクト フォーラム
  - https://productforums.google.com/forum/#!topic/chrome/m_sMBfiYUms
- document.createElement - Web API インターフェイス | MDN
  - https://developer.mozilla.org/ja/docs/Web/API/Document/createElement


でも、innerHTMLはsetterなので、上記が落ち着くまでは、

```
var hoge = xxxxx.__lookupSetter__("innerHTML");
hoge.call(element, "<b>hoge</b>");
```

みたいになる心配は無さそう。プロパティだしこれも無いかな。

プロパティとして一般に使うのであれば `xxxxx.innerHTML=<string>` 形式で使うのは同じなので、多分この形式を探してブレークを仕掛けられれば捉えられると思う（←結局何をしたかったのか？）。

***

多分今なら、「how to hook Element innerHTML property」とか、「how to hook document.write」とかでググればいろいろ出てくるのではないか。それで何を作ろうとしていたのか忘れてしまったけれども・・・。なんだろ、仕事に関連した一種のJavaScriptのプロファイラでも作ろうとしてたのかな？