てくのーと
634 文字
3 分

MDXでHTML,CSS,JSをブラウザで実行するAstroコンポーネントを作る

2024-05-14
2024-05-28

前回の記事では、CSSのコンテナクエリに関する記事を書きました。
その中で、MDXでHTML,CSS,JSをMDX内で書き、ブラウザでレンダリングする必要があったので、それを実現するコンポーネントを作成しました。

今回は、そのコンポーネント(TextToHTMLという名前にしています)について紹介します。

使用例#

{/* 1. TextToHTMLコンポーネントを読み込む */}
import TextToHTML from 'src/components/markdown/TextToHTML.astro'


{/* 2. TextToHTMLコンポーネントに必要なデータを渡す */}
<TextToHTML
css=`
.box {
    padding: 10px;
    background-color: lightblue;
}
.text,
.js-result {color: #333;}
`
{/* 3. 他のコンポーネントに影響を与えないようにする都合上shadowDocumentという変数を用意して、アクセスできるようにしています */}
script=`
shadowDocument.querySelector(".js-result").innerText = shadowDocument.querySelector(".text").innerText + " World!"
`
>
    <div class="box">
        <p class="text">Hello</p>
        <div class="js-result"></div>
    </div>
</TextToHTML>

実際に上記のコードを実行すると、以下のようになります。

解説#

以下のものがTextToHTMLコンポーネントのソースコードです。見ての通り、受け取った文字列のhtml, css, jsを愚直にレンダリングしているだけです。

工夫した点としては、CSS,JSが外部のHTMLに影響を与えないようにするためにShadow DOMを利用している点です。
idは、クライアント簡易的に生成しています。(絶対に衝突したくないのであれば、uuidを利用した方がいいです。)

少し気になる点としては、コンポーネントを利用する側から、documentで要素に楽にアクセスできない点です。
なので少し面倒ではありますが、shadowDocumentという変数を用意しており、それを指定することでShadow DOM内の要素にアクセスできるようにしています。

注意

このコードを参考に実装する場合には、JS実行のためにFunctionを利用している点に注意が必要です。
JS部分の入力が外部からも可能である場合、Functionは任意のJavaScriptコードを実行するため、悪意のあるコードが実行されるリスクがあります。そのためこの実装はしてはいけません。

---
// slotで受け取る
type Props = {
	css?: string;
	script?: string;
};
const { css, script } = Astro.props;
const html = await Astro.slots.render("default");
const id = "astro-shadow-html-" + Math.random().toString(36).slice(2);
---

<div id={id} class="mb-5"></div>

<script define:vars={{ id, html, css, script }}>
	const container = document.querySelector("#" + id);
	const shadowRoot = container.attachShadow({ mode: "open" });
	shadowRoot.innerHTML = html;

	let sheet = new CSSStyleSheet();
	sheet.replaceSync(css);
	shadowRoot.adoptedStyleSheets.push(sheet);

    const shadowDocument = shadowRoot;
    new Function('shadowDocument', `'use strict'; ${script}`)(shadowDocument);
</script>
\てくのーと おすすめ書籍!/

プログラミングを楽しみ続けるためには健康は不可欠!
本書では如何に健康であり続けるかが科学的な情報とともに紹介されています。
→感想詳細はこちら!