てくのーと
634 文字
3 分

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

2024-05-14
2024-07-05

前回の記事では、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>
\てくのーと おすすめ書籍!/

OAuthという名前は知っているけれど、中身のフローは知らないという方におすすめです。
丁寧に書かれているので、理解がスムーズに進みます。
→感想詳細はこちら!