---
title: 平成のjQueryに令和のJSXを
tags: 
author: [murasuke](https://www.docswell.com/user/4962106)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/LE3WZ8NGE5.jpg?width=480
description: React等フレームワークを使わず、自力でJSXをDOMに変換してみます。
published: June 15, 26
canonical: https://www.docswell.com/s/4962106/ZMQ638-2026-06-15-201630
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/LE3WZ8NGE5.jpg)

平成のjQueryに令和の JSXを
$(&#039;ul&#039;).append(&lt;li&gt;追加リスト&lt;/li&gt;)
2026/06/15 俺たちの勉強会 #6
#orestudy


# Page. 2

![Page Image](https://bcdn.docswell.com/page/8EDKRMWN7G.jpg)

自己紹介： murasuke
murasuke(むらすけ) 株式会社ツールラボ 開発部 部長
SE/プログラマーとして20年、多種多様なシステムを構築
プログラムが書きたいので転職しました (現職)
1年前、人生初のイベント参加(PHPカンファレンス名古屋)を
きっかけにして、フロカン名古屋で初登壇 してきました！


# Page. 3

![Page Image](https://bcdn.docswell.com/page/V7PKWR9NJ8.jpg)

はじめに
● 皆さん、 jQuery使ってますか？
● JSX書いてますか？
● 両方同時に使ったことある (酔狂な)人いますか！！？？
ご存じの通り、ReactとjQueryは相性が良くありません。
ReactはVirtual DOMを管理し、jQueryは直接DOMを操作するためです。
render()のタイミングでDOMが再生成され、jQueryのコードは消えてしまいます。


# Page. 4

![Page Image](https://bcdn.docswell.com/page/2JVV8RMYJQ.jpg)

JSXはVirtual DOMを作るための仕組みではない
JSXは単なる関数呼び出しなので、 その関数が Virtual DOM を返すか、
実DOMを返すかは実装次第です。
そこで今回は
jQueryから操作可能な、実 DOMを生成する JSX
という、「いいとこどり？」をやってみます！
React等フレームワーク は使いません。
実DOMを返す「jsx-runtime」を自作して、JSXを自らの手 に取り戻しましょう！


# Page. 5

![Page Image](https://bcdn.docswell.com/page/5EGL5MGWJL.jpg)

JSX 基礎
JSXは「HTMLっぽい見た目をした、関数呼び出し(シンタックスシュガー)」です。
JSX はJavaScriptやブラウザの機能ではありません。
TypeScriptやBabel によって、ただの「関数呼び出し 」に変換されます。
例えば、このような感じです。
&lt;!-- JSX --&gt;
h(
&lt;div className=&quot;box&quot;&gt;
&quot;div&quot;,
&lt;h1&gt;Hello&lt;/h1&gt;
{ className: &quot;box&quot; },
&lt;/div&gt;
h(&quot;h1&quot;, null, &quot;Hello&quot;)
);
続いて「h(tag, props, childlen)」でDOMを生成して返す処理を実装していきます。


# Page. 6

![Page Image](https://bcdn.docswell.com/page/4JQYZRXQ7P.jpg)

JSX 基礎
旧方式（ classic runtime）
jsxFactoryで指定した関数に変換されます（cf. React.createElement()）
h(
{
&quot;jsx&quot;: &quot;react&quot;,
&quot;div&quot;,
&quot;jsxFactory&quot;: &quot;h&quot;
{ className: &quot;box&quot; },
}
h(&quot;h1&quot;, null, &quot;Hello&quot;)
);
新方式（ automatic runtime）
TypeScriptがjsx-runtimeのimportを自動で挿入します
import { jsx as _jsx } from ./runtime/jsx-runtime&quot;;
{
&quot;jsx&quot;: &quot;react-jsx&quot;,
&quot;jsxImportSource&quot;: &quot;./runtime&quot;
}
_jsx(&quot;div&quot;, {
className: &quot;box&quot;,
children: _jsx(&quot;h1&quot;, {children: &quot;Hello&quot;})
});
多少の違いはありますが、本質的には変わりありません


# Page. 7

![Page Image](https://bcdn.docswell.com/page/K74W386YE1.jpg)

JSXから実DOMへの変換
それでは関数 「h(tag, props, children)」 を定義し、
実DOMを生成する処理を実装して行きましょう！
&lt;Border&gt;
{counter}
&lt;button style={{ color: &#039;blue&#039;, margin: &#039;5px&#039; }} onclick={handleInc}&gt;Increment&lt;/button&gt;
&lt;/Border&gt;
① JSX ⇒ 関数への変換（トランスパイル）
② h(tag, props, children) 関数を実行して DOMを生成する
&lt;div style=&quot;border: 1px solid black; padding: 5px; display: inline-block;&quot;&gt;
1
&lt;button style=&quot;color: blue; margin: 5px;&quot; onclick=&quot;handleInc()&quot;&gt;Increment&lt;/button&gt;
&lt;/div&gt;


# Page. 8

![Page Image](https://bcdn.docswell.com/page/LJ1Y12VNEG.jpg)

1. タグ名が関数なら実行
大文字で始まるタグはFunctionに変換されるので、引数(props, children)をつけてそのま
ま呼び出します。
/* function h(tag: any, props: any, ...children: any[]) { */
if (typeof tag === &#039;function&#039;) {
return tag({
...(props || {}),
children,
});
}
※大文字で始まる「タグ名」に対応する「関数コンポーネント」は別途
個別に実装します。


# Page. 9

![Page Image](https://bcdn.docswell.com/page/GJWG8R3M72.jpg)

2. 普通のタグなら Element 作成
関数コンポーネント以外は、そのままElementを作成します。
/* function h(tag: any, props: any, ...children: any[]) { */
// elementを作成
const elm = document.createElement(tag);


# Page. 10

![Page Image](https://bcdn.docswell.com/page/4EZL8RVM73.jpg)

3. props を属性へ変換
`on`から始まり、値が関数 の場合addEventListener()でイベントに紐づけます
if (key.startsWith(&#039;on&#039;) &amp;&amp; typeof value === &#039;function&#039;) {
const eventName = key.slice(2).toLowerCase();
elm.addEventListener(eventName, value);
}
`style`の場合は、elementのstyleにマージ、`className`なら「class」として追加
if (key === &#039;style&#039; &amp;&amp; typeof value === &#039;object&#039;) {
Object.assign(elm.style, value);
} else if (key === &#039;className&#039;) {
elm.setAttribute(&#039;class&#039;, value);
} else if (value != null &amp;&amp; value !== false) {
// 上記以外の属性を追加
elm.setAttribute(key, String(value));
}
余談：JSX的には「className」ではなく「class」でも大丈夫です (Reactの慣例に従っている )


# Page. 11

![Page Image](https://bcdn.docswell.com/page/Y76WPR557V.jpg)

4. children を append
JSXでは`children`に下記を含むことができます
●
Node、 文字列、 配列、null、 boolean
&lt;div&gt;
&lt;div&gt;hello123&lt;/div&gt;
hello
{[1,2,3]}
&lt;/div&gt;
そのため種類ごとに処理を分ける必要があります
配列：再帰的に`appendChildren()`を適用
boolean：処理をスキップ(追加しない)
Node：そのまま`appendChild()`
文字列：createTextNode()でNodeを作成してから追加


# Page. 12

![Page Image](https://bcdn.docswell.com/page/G75MK8YG74.jpg)

4. children を append
function appendChildren(parent: Node, children: any) {
if (Array.isArray(children)) {
children.forEach((child) =&gt; appendChildren(parent, child));
return;
}
if (children == null || children === false || children === true) {
return;
}
if (children instanceof Node) {
parent.appendChild(children);
return;
}
parent.appendChild(document.createTextNode(String(children)));
}
種類毎に処理を分けて、追加していきます


# Page. 13

![Page Image](https://bcdn.docswell.com/page/9J29WKGDER.jpg)

ランタイム全体像
export function h(tag: any, props: any, ...children: any[]) {
} else if (key === &#039;style&#039; &amp;&amp; typeof value === &#039;object&#039;) {
if (typeof tag === &#039;function&#039;) {
// styleの追加
// 大文字で始まるタグ(関数コンポーネント)を実行
Object.assign(elm.style, value);
return tag({
} else if (key === &#039;className&#039;) {
...(props || {}),
// classの追加
children,
elm.setAttribute(&#039;class&#039;, value);
});
} else if (value != null &amp;&amp; value !== false) {
}
// 上記以外の属性を追加
// elementを作成
elm.setAttribute(key, String(value));
const elm = document.createElement(tag);
}
}
// 属性を追加
}
if (props) {
for (const [key, value] of Object.entries(props)) {
// 子要素の追加
if (key.startsWith(&#039;on&#039;) &amp;&amp; typeof value === &#039;function&#039;) {
appendChildren(elm, children);
// イベントハンドラの追加
return elm;
const eventName = key.slice(2).toLowerCase();
elm.addEventListener(eventName, value);
}


# Page. 14

![Page Image](https://bcdn.docswell.com/page/DEY4LNVMJM.jpg)

jQuery と組み合わせる
ここでようやく本題です。
この「 h()」が返すのは「本物のDOM (HTMLElement)」です。
なので普通に 「jQuery」に渡せます。
$(&#039;ul&#039;).append(&lt;li&gt;追加リスト&lt;/li&gt;);
ついに「 jQueryとJSX」が、手を繋ぎあってくれました！


# Page. 15

![Page Image](https://bcdn.docswell.com/page/VJNY4XM378.jpg)

型安全の恩恵も受けられる
例えば、jQueryでは単に文字列なのでタイプミスを検出できませんが、
でも、JSXならTypeScriptがエラー(タイプミス)を検出してくれます。
$(&#039;#app&#039;).append(&lt;div clasName=&quot;box&quot;&gt; as any);
さらにこのあたりも利用できるようになります。
● 補完
● lint
ただし、jQueryはTypeScriptの型定義と相性が悪く、エラーだらけになるので
jQuery側は「any」を多用することになります。
余談：jQueryなのにビルドが必要という時点で、あまり実用的ではないですよね・・・


# Page. 16

![Page Image](https://bcdn.docswell.com/page/YE9PQ8NPJ3.jpg)

動作サンプル
「Increment」ボタンをクリックすると+１するカウンターコンポーネント
export type Child = | Node | string | number | boolean
| null | undefined | Child[];
export type Props = {
style?: Partial&lt;CSSStyleDeclaration&gt;;
children?: Child;
型定義
Child：Node、文字、自身の配列
Props：css定義、Child以外は任意
[key: string]: unknown;
};
function Border({ children, ...props }: Props) {
return (
&lt;div {...props}
style={{
border: &#039;1px solid black&#039;, padding: &#039;5px&#039;,
display: &#039;inline-block&#039;,
}}
&gt; {children} &lt;/div&gt;
);
}
Border
childrenに枠をつけるコンポーネント


# Page. 17

![Page Image](https://bcdn.docswell.com/page/GE8DG8KXED.jpg)

動作サンプル
「Increment」ボタンをクリックすると+１するカウンターコンポーネント
let count = 0;
const counter = (&lt;div&gt;Count: {count}&lt;/div&gt;) as HTMLDivElement;
const handleInc = () =&gt; {
counter.innerText = `Count: ${++count}`;
};
const elements = (
&lt;Border&gt;
{counter}
&lt;button id=&quot;increment-button&quot;
onclick={handleInc}&gt;Increment&lt;/button&gt;
&lt;/Border&gt;
);
$(&#039;#app&#039;).append(elements as any);
$(&#039;#increment-button&#039;).css(&#039;color&#039;, &#039;blue&#039;).css(&#039;margin&#039;, &#039;5px&#039;);
●
&lt;Border&gt;による枠の追加
●
jQueryによるDOM追加
●
onclickによるイベントハンドラ
●
jQueryによるスタイル定義
ともに想定した通りに動きました！


# Page. 18

![Page Image](https://bcdn.docswell.com/page/LELMGRZN7R.jpg)

まとめ
今回は 「jsx-runtime を自作し、 jQuery と JSX を共存させる」
と言う話をしました。
●
JSX はただの関数呼び出し
●
コンポーネントは関数
●
JSX は自分で作れる
●
実DOMを返せば jQuery と共存可能
自作「 jsx-runtime」を通して、ReactやJSXの仕組みを「チョットダケ 」
理解できたのではないでしょうか？


