そのページ自身をダウンロードする関数
#tech
普通やらないと思うが、以下の関数でそのページの HTML がダウンロードできる。
React などを使っている場合でも実際の DOM にレンダリングされた要素はレンダリングされた状態で出力される。
CSS も有効にしたいなら style-loader などで最初から <style> に挿入するなりインライン化するなりしておくのが楽。
code:ts
const download: () => void = () => {
const doc = document.documentElement.cloneNode(true) as HTMLElement;
const script = doc.getElementsByTagName("script")0;
script.parentNode!.removeChild(script);
const html = <!DOCTYPE html>\n${doc.outerHTML};
const a = document.createElement("a");
a.href = URL.createObjectURL(new Blob(html, { type: "text/html" }));
a.download = "page.html";
a.dispatchEvent(new MouseEvent("click"));
};
<script> を残していいなら以下のようにしてもよい。
code:ts
const html = <!DOCTYPE html>\n${document.documentElement.outerHTML};
doc を整形する際に <canvas> を <img> に変換するなら、こんな処理も必要。
code:ts
const img = document.createElement("img");
const canvas = doc.getElementsByTagName("canvas")0;
img.src = document.getElementsByTagName("canvas")0.toDataURL();
img.className = canvas.className;
canvas.parentNode!.replaceChild(img, canvas);
解説
まずは document.documentElement で <html> 要素を取得する。
ここから <body> の <script> を取り除くが、document.body から <script> を removeChild() すると、最初の実行でそのコード自体も消えてしまうため初回しか動作しない。
これを防ぐために cloneNode() で document.documentElement を深くコピーし、これに対して removeChild() をする。
あとは outerHTML で <html> 要素の文字列表現を取り出し、リンクを作って <a> 要素の href にこれを指定する。
最後の行は a.click() だと Chrome では動くが Firefox では動かないため、dispatchEvent() でイベントを発火させている。
クリックイベントの起こし方は MDN に例がある ので、これを参考に書いた。
参考URL