直接用字串渲染 DOM?
緣起
故事是這樣的:有一串像是 HTML 的文字給你,然後你要用它渲染成 HTML。這要怎麼做呢?
這個情境很好用:如果後端要給你 HTML 文字、或是想玩玩爬蟲或其他功能的話,其實都很關鍵。
題外話,後端要給 HTML 文字,之後寫一篇來講。
機制
那麼瀏覽器有沒有呢?還真有:DOMParser
。這個物件能把輸入的字串變成有效的 HTML DOM 文件。
接著我們可以這樣做:parseFromString(string, type)
──
string
就是輸入的字串。- 而
type
可以決定渲染的類別。如果要渲染 HTML 字串的話,使用"text/html"
就好。
例如 MDN 提供的:
const parser = new DOMParser();
const htmlString = "<strong>Beware of the leopard</strong>";
const doc3 = parser.parseFromString(htmlString, "text/html");
console.log(doc3.body.firstChild.textContent);
這個範例可以顯示 Beware of the leopard
這文字。
實做
現在,假設我們想要 MDN 站內的連結,都顯示簡介。
我們先寫一個請求站內 HTML 字串的函式:
const get_html_string = async (url) =>
{
const request = await fetch(url).then(res => res.text());
return request;
};
這樣就可以用非同步的方法,去請求站內 HTML 字串了。
如果是站外的連結,因為有 CORS 的問題,所以可能比較不太可能。
總之,再繼續從 https://developer.mozilla.org/en-US/docs/Web/API/AbortController 觀察我們需要的文字。
(圖……再看看吧)
觀察下來,我們需要的是「The AbortController interface represents a controller object that allows you to abort one or more Web requests as and when desired.」這段文字。觀察一下,就用 document.querySelector(".main-page-content p").innerText
取得吧。
const parse_string = (html) =>
{ // `?.` 可以讓 undefined 的物件被略過
const doc = new DOMParser().parseFromString(html, "text/html");
return doc.querySelector(".main-page-content p")?.innerText ?? "";
};
然後把他們接在一起:
const main = async (url) =>
{
const html = await get_html_string(url);
const the_string = parse_string(html);
return parse_string(html);
};
再把文字綁定在 title 屬性:
const add_title = async(dom) =>
{
const t = await main(dom.href);
dom.title = t;
};
最後只要把所有合條件的連結,都呼叫 add_title
就可以囉:
const links = [...document.querySelectorAll("a")].filter(a=> /developer.mozilla.org/g.test(a.href) );
links.forEach( add_title(dom) );
實做 TL;DR
合起來就是這樣:
const get_html_string = async (url) =>
{
const request = await fetch(url).then(res => res.text());
return request;
};
const parse_string = (html) =>
{ // `?.` 可以讓 undefined 的物件被略過
const doc = new DOMParser().parseFromString(html, "text/html");
return doc.querySelector(".main-page-content p")?.innerText ?? "";
};
const main = async (url) =>
{
const html = await get_html_string(url);
const the_string = parse_string(html);
return the_string;
};
const add_title = async(dom) =>
{
const t = await main(dom.href);
dom.title = t;
};
const links = [...document.querySelectorAll("a")].filter( a =>
/developer.mozilla.org/g.test(a.href)
);
links.forEach( dom => add_title(dom) );
這程式還有很多問題,不過先到此為止。
其他
那 XSS 呢?
XSS:只要把 HTML 貼到文件內並執行 JavaScript 的話,執行的 JavaScript 可以被加料,然後搞爛你的電腦。
我看了一下,結論是 DOMParser
有沙盒機制能阻止 XSS。只要不白目的把東西貼到 innerHTML 的話,看起來應該是可以的。
node.js 能不能用?
看來不行。因為有很多機制要處理。
另,我個人推薦裡面列出的 jsdom 套件。
結語
直接用字串渲染為 DOM 的話,你可以用 DOMParser
API。這個 API 用途廣泛,是個相當不錯的前端功能。