この記事でできること
- D3.js v7を使って補助金の補助額比較・採択率推移・政府予算推移を描画する手順を理解できる
- WordPressにD3.jsを組み込む際の3つの必須対応(CDNタグ禁止・
&&禁止・wp:htmlラッパー)が分かる - 異なる出典のデータを単一チャートに統合するときの設計上の注意点を学べる
- 実際に動作するツールはAI補助金・助成金ファインダー(AI Japan Index)で確認できる
環境・前提
- ブラウザ: Chrome 123+ / Firefox 125+ / Safari 17+
- D3.js: v7(CDNからの動的ロード)
- WordPress: 6.5以降(SWELLテーマ使用環境を想定)
- JavaScript: ES2020(Vanilla)
- 追加のビルドツールなし(webpack/Vite等不要)
完成形
政府AI関連予算の推移(2018〜2024年度)、IT導入補助金の採択率推移(2020〜2024年度)、AI補助金7制度の最大補助額比較の3チャートをインタラクティブに表示できる状態になる。動作確認はAI補助金・助成金ファインダーで行える。
手順
Step 1: WordPressでD3.jsを動的ロードする(CDNタグは使えない)
WordPressのコンテンツ内で <script src="CDN"> を使ってはいけない。WordPress/SWELLはコンテンツ内の外部スクリプトタグを無視するため、D3.jsが読み込まれずに描画が一切されない。
NG例(WordPress環境では動かない):
<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
<script>
// d3 is undefined → エラー
const svg = d3.select("#chart");
</script>
OK例(動的ロードで解決):
function loadD3(callback) {
if (typeof d3 !== "undefined") {
// すでにロード済みなら即コールバック
callback();
return;
}
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js";
script.onload = callback;
script.onerror = function() {
document.getElementById("subsidy-error").style.display = "block";
};
document.head.appendChild(script);
}
document.addEventListener("DOMContentLoaded", function() {
loadD3(initCharts);
});
このコードの意味: typeof d3 でロード済みかを確認し、未ロードなら createElement("script") で動的に追加する。onload で描画初期化関数を呼び出す。
Step 2: && 演算子を三項演算子に置き換える
WordPressのコンテンツフィルターは && を && にHTMLエンティティ変換する。これはJSの構文エラーになりスクリプト全体が停止する。
NG例(WordPressで壊れる):
const label = isMobile && showLabel ? data.name : "...";
const margin = { left: isMobile && hasYAxis ? 200 : 20 };
OK例(三項演算子またはネストifで代替):
const label = isMobile ? (showLabel ? data.name : "...") : data.name;
const leftMargin = isMobile ? 20 : 200;
const margin = { left: leftMargin };
つまずきポイント: この変換はWordPressの管理画面から投稿を保存するタイミングで発生する。ローカルHTMLでは問題なく動作するのに、WordPress投稿後だけ動かない——という現象はこれが原因。
Step 3: データ構造を設計する(出典定義を統一する)
補助金データは複数の出典から収集する。同一チャートに混入させる場合、データの「定義・スコープ」が一致しているかを確認する。
// 政府AI予算推移データ(内閣府AI戦略会議定義に統一)
// ※SIP/BRIDGE等一部施策分は含まない(内閣府AI戦略会議第5回資料に準拠)
const budgetTrend = [
{ year: 2018, budget: 575.5 },
{ year: 2019, budget: 770.4 },
{ year: 2020, budget: 1178.4 },
{ year: 2021, budget: 1168.3 },
{ year: 2022, budget: 1024.9 },
{ year: 2023, budget: 1126.7 },
{ year: 2024, budget: 1138.4 }
];
// lastVerified: "2026-03-30"
// sourceUrl: "https://www8.cao.go.jp/cstp/ai/ai_senryaku/5kai/shisaku.pdf"
// 補助額比較データ(各制度公式要領に基づく最大補助額)
const subsidyData = [
{ name: "省力化投資補助金(一般型)", maxAmount: 10000, rate: "1/2〜2/3" },
{ name: "新事業進出補助金", maxAmount: 9000, rate: "1/2〜2/3" },
{ name: "ものづくり補助金", maxAmount: 3500, rate: "1/2〜2/3" },
{ name: "省力化投資補助金(カタログ型)", maxAmount: 1500, rate: "1/2" },
{ name: "事業承継・M&A補助金", maxAmount: 800, rate: "1/2〜2/3" },
{ name: "デジタル化・AI導入補助金", maxAmount: 450, rate: "1/2〜4/5" },
{ name: "小規模事業者持続化補助金", maxAmount: 200, rate: "2/3〜3/4" }
];
ポイント: 経産省の「AI・半導体関連予算1兆2,390億円」(2026年度)は定義が内閣府とは異なる広い範囲を含むため、上記の時系列に混入させない。別の参考値として注記付きで表示する。
Step 4: 横棒チャートを描画する(対数スケール対応)
補助額の最大値(1億円)と最小値(200万円)の比率が50倍になるため、線形スケールでは小さいバーが潰れる。対数スケールを使う。
function drawSubsidyChart(data) {
const isMobile = window.innerWidth <= 767;
const margin = {
top: 20, right: 80,
bottom: 50,
left: isMobile ? 15 : 220
};
const width = document.getElementById("subsidy-chart").clientWidth;
const height = 320;
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const svg = d3.select("#subsidy-chart")
.append("svg")
.attr("width", width)
.attr("height", height);
const g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// 対数スケール(最小値を少し下げてバーが0から始まるように見せる)
const xScale = d3.scaleLog()
.domain([100, 12000]) // 100万円〜12,000万円
.range([0, innerWidth]);
const yScale = d3.scaleBand()
.domain(data.map(d => d.name))
.range([0, innerHeight])
.padding(0.25);
// バーを描画
g.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", 0)
.attr("y", d => yScale(d.name))
.attr("width", d => xScale(d.maxAmount))
.attr("height", yScale.bandwidth());
// PCのみY軸ラベルを表示
if (!isMobile) {
g.selectAll(".y-label")
.data(data)
.enter()
.append("text")
.attr("class", "y-label")
.attr("x", -8)
.attr("y", d => yScale(d.name) + yScale.bandwidth() / 2)
.attr("text-anchor", "end")
.attr("dominant-baseline", "middle")
.text(d => d.name);
}
}
Step 5: スマホ対応のタッチイベント(下部固定パネル)
スマホではY軸ラベルを非表示にし、バーをタップしたときに画面下部固定パネルで詳細を表示する。
// 下部パネルのHTML(ツールのHTMLに事前配置)
// <div id="mobile-panel" style="display:none; position:fixed; bottom:0; left:0;
// width:100%; background:#1a1a2e; border-top:2px solid #f26419;
// padding:12px 16px; z-index:10000; max-height:40vh; overflow-y:auto;">
// <button id="panel-close">×</button>
// <div id="panel-content"></div>
// </div>
function showMobilePanel(name, amount, rate) {
document.getElementById("panel-content").innerHTML =
"<strong>" + name + "</strong><br>" +
"最大補助額: " + amount + "<br>" +
"補助率: " + rate;
document.getElementById("mobile-panel").style.display = "block";
}
// バーへのタッチイベント登録
bars.on("touchstart", function(event, d) {
event.preventDefault();
event.stopPropagation(); // ← 省略するとパネルが開いて即閉じる
showMobilePanel(d.name, d.maxAmount + "万円", d.rate);
});
// パネル外タップで閉じる
document.addEventListener("touchstart", function(event) {
const panel = document.getElementById("mobile-panel");
if (panel.style.display === "block") {
panel.style.display = "none";
}
});
document.getElementById("panel-close").addEventListener("click", function() {
document.getElementById("mobile-panel").style.display = "none";
});
つまずきポイント: e.stopPropagation() を省略すると「パネルが表示された直後に閉じる」現象が起きる。バーのタッチイベントが document まで伝播し、「パネル外タップ」と誤判定されるためだ。必ずセットで書く。
Step 6: wp:htmlラッパーで全体をまとめる(DOCTYPE禁止)
WordPressへの貼り付けは <!-- wp:html --> ブロックで全体を囲む。<!DOCTYPE html> や <html> タグから始まるフルHTML構造は使わない(wpautopフィルターがscript内に <p> タグを挿入してJSを破壊する)。
<!-- wp:html -->
<div class="aji-subsidy-finder-wrap">
<style>
.aji-subsidy-finder-wrap { /* スコープ付きCSS */ }
.aji-subsidy-finder-wrap .bar { fill: #f26419; }
/* 全セレクタを .aji-subsidy-finder-wrap で囲む。グローバルCSS禁止 */
</style>
<div id="subsidy-chart"></div>
<div id="mobile-panel" style="display:none; ...">...</div>
<script>
(function() {
// D3動的ロード + 全チャート描画コード
})();
</script>
</div>
<!-- /wp:html -->
つまずきポイント: body {} や h1 {} 等のグローバルCSSセレクタを使うとSWELLテーマ全体のスタイルが壊れる。全セレクタを .aji-subsidy-finder-wrap で囲む必要がある。
つまずきポイントまとめ
-
D3が動かない(WordPress):
<script src="CDN">はWordPressに無視される → 動的ロードに変更 -
&&の後でスクリプトが止まる: WordPressが&&を&&に変換 → 三項演算子に置換 -
スマホでラベルが重なる: 日本語ラベルはスマホ幅に入らない →
if (!isMobile)で非表示にしてタッチパネル表示に切り替え - バーがずれる: scaleBandのドメインとバー配置のアクセサが不一致 → 同じキーを使う
-
タッチパネルが即閉じる:
e.stopPropagation()が抜けている → タッチイベントに必ず追加 -
スタイルが全ページに影響する: グローバルCSSを使っている →
.aji-wrap等のスコープセレクタで囲む
まとめ
- D3.js v7のSVG描画方式でWordPressのChart.js競合問題を回避できた
-
&&禁止・CDNタグ禁止・wp:htmlラッパーの3点がWordPressでのD3実装必須事項 - 200倍の差があるデータ比較には対数スケールが有効
- scaleBandはドメインとアクセサキーを一致させる
-
e.stopPropagation()なしのタッチイベントはパネルが即閉じる
実装の動作確認はAI補助金・助成金ファインダー(AI Japan Index)で行える。類似のデータビジュアライゼーションツールはAI Japan Indexのツール一覧にまとまっている。
