この記事でできること
- D3.js v7を使ってバブルチャート・対数スケール横棒・ツリーマップ・レンジバーをWordPressに組み込む手順が分かる
- WordPressにD3.jsを組み込む際の3つの必須対応(CDNタグ禁止・
&&禁止・wp:htmlラッパー)の理由と実装方法が分かる - 80倍差があるデータ(米国4,709億ドル vs 日本59億ドル)を対数スケールで正しく表現する方法が分かる
- スマホでのバブルチャートY軸ラベルをHTML divで実装する方法が分かる
- 実際に動作するツールはAIスタートアップ資金調達マップ(AI Japan Index)で確認できる
環境・前提
- ブラウザ: Chrome 123+ / Firefox 125+ / Safari 17+
- D3.js: v7(CDNからの動的ロード)
- WordPress: 6.5以降(SWELLテーマ使用環境を想定)
- JavaScript: ES2020(Vanilla)
- 追加のビルドツールなし(webpack/Vite等不要)
完成形
日本のAIスタートアップ資金調達状況を以下の5チャートで表示できる状態になる。
- A. 年別投資トレンド(棒グラフ、推計値はハッチング表示、2019〜2025年)
- B. TOP企業バブルチャート(設立年×事業領域×累計調達額、カテゴリフィルター付き)
- C. 事業領域別ツリーマップ(領域ごとの調達額シェア)
- D. ラウンド別傾向(レンジバー横棒、シード〜シリーズD以降)
- E. 国際比較(対数スケール横棒、5カ国累計AI民間投資額)
動作確認は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;
}
var 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("funding-error").style.display = "block";
};
document.head.appendChild(script);
}
document.addEventListener("DOMContentLoaded", function() {
loadD3(initAllCharts);
});
つまずきポイント: ローカルのHTMLファイルで開発すると <script src> が正常に動作するため、WordPress上だけで再現するバグに気づきにくい。開発段階から動的ロード方式で書いておくと問題が起きない。
Step 2: && 演算子を三項演算子またはネストifに置き換える
WordPressのコンテンツフィルターは && を && にHTMLエンティティ変換する。これはJSの構文エラーになりスクリプト全体が停止する。
NG例(WordPressで壊れる):
var label = isMobile && data.name.length > 10 ? shortLabel : data.name;
var leftMargin = isMobile && hasYAxis ? 200 : 20;
OK例(ネストifまたは三項演算子で代替):
// ネストif版
var label = data.name;
if (isMobile) {
if (data.name.length > 10) { label = shortLabel; }
}
// 三項演算子版(&&を含まない形)
var leftMargin = isMobile ? 20 : 200;
つまずきポイント: && の変換はWordPressの管理画面からHTMLを保存するタイミングで発生する。投稿保存のたびに壊れるため、保存後は必ずブラウザでJSエラーを確認すること。
Step 3: データ構造の設計(推計値フラグと出典URLの必須化)
推計値と確定値を区別するためのデータ構造を設計する。
// 年別トレンドデータ(推計値にisEstimateフラグ)
var yearlyTrend = [
{ year: 2019, totalBillionYen: 4462, isEstimate: true,
sourceUrl: "https://initial.inc/information/japan-startup-finance-2023" },
{ year: 2020, totalBillionYen: 4535, isEstimate: true,
sourceUrl: "https://initial.inc/information/japan-startup-finance-2023" },
{ year: 2021, totalBillionYen: 7801, isEstimate: false,
sourceUrl: "https://initial.inc/information/japan-startup-finance-2023" },
{ year: 2022, totalBillionYen: 9664, isEstimate: false,
sourceUrl: "https://initial.inc/information/japan-startup-finance-2023" },
{ year: 2023, totalBillionYen: 7536, isEstimate: false,
sourceUrl: "https://initial.inc/information/japan-startup-finance-2023" },
{ year: 2024, totalBillionYen: 7793, isEstimate: false,
sourceUrl: "https://prtimes.jp/main/html/rd/p/000000267.000010548.html" },
{ year: 2025, totalBillionYen: 7600, isEstimate: true,
sourceUrl: "https://initial.inc/articles/japan-startup-finance-2025h1" }
];
// lastVerified: "2026-03-31"
推計値バーはSVGのハッチングパターンで視覚的に区別する。
// SVG defs内にハッチングパターンを定義
var defs = svg.append("defs");
defs.append("pattern")
.attr("id", "hatch-estimate")
.attr("patternUnits", "userSpaceOnUse")
.attr("width", 8)
.attr("height", 8)
.append("path")
.attr("d", "M-1,1 l2,-2 M0,8 l8,-8 M7,9 l2,-2")
.attr("stroke", "#9369A8")
.attr("stroke-width", 1.5)
.attr("opacity", 0.5);
// 推計値バーと確定値バーで塗り分け
bars.attr("fill", function(d) {
return d.isEstimate ? "url(#hatch-estimate)" : "#9369A8";
});
Step 4: 対数スケールで80倍差を可視化する(国際比較チャート)
国際比較チャートは米国累計4,709億ドル vs 日本59億ドルで約80倍の差がある。線形スケールでは日本のバーが視認できないため、対数スケールを使う。
function drawGlobalChart(data) {
var isMobile = window.innerWidth <= 767;
var margin = {
top: 20, right: 100,
bottom: 60,
left: isMobile ? 15 : 80
};
var width = document.getElementById("global-chart").clientWidth;
var height = 280;
var innerWidth = width - margin.left - margin.right;
var innerHeight = height - margin.top - margin.bottom;
var svg = d3.select("#global-chart")
.append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// 対数スケール: domainの最小値を1にしてlog(0)エラーを防ぐ
var xScale = d3.scaleLog()
.domain([1, d3.max(data, function(d) { return d.cumulativeBillionUsd; }) * 1.3])
.range([0, innerWidth])
.nice();
var yScale = d3.scaleBand()
.domain(data.map(function(d) { return d.country; }))
.range([0, innerHeight])
.padding(0.3);
// バーの開始点をx(1)にする(0からではなく1から描画)
g.selectAll(".bar")
.data(data).enter()
.append("rect")
.attr("class", "bar")
.attr("x", xScale(1))
.attr("y", function(d) { return yScale(d.country); })
.attr("width", function(d) { return xScale(d.cumulativeBillionUsd) - xScale(1); })
.attr("height", yScale.bandwidth())
.attr("fill", "#9369A8");
// PCのみY軸ラベル表示
if (!isMobile) {
g.selectAll(".y-label")
.data(data).enter()
.append("text")
.attr("class", "y-label")
.attr("x", -8)
.attr("y", function(d) { return yScale(d.country) + yScale.bandwidth() / 2; })
.attr("text-anchor", "end")
.attr("dominant-baseline", "middle")
.text(function(d) { return d.country; });
}
}
つまずきポイント: d3.scaleLog().domain([0, max]) はlog(0)が-∞になりエラーが発生する。domainの最小値は必ず1以上にすること。バーの開始点も x(0) ではなく x(1) にする。
Step 5: バブルチャートのY軸ラベルをHTML divで実装する
バブルチャートのY軸ラベル(「フィジカルAI・ロボティクス」「基盤モデル・LLM」等の日本語10〜15文字)はSVG textではなくHTML divで実装する。SVG textは自動改行不可で、スマホ幅では必ず切れるためだ。
// バブルチャートのY軸ラベルをHTML divで実装
if (!isMobile) {
var ylabelsDiv = document.getElementById("bubble-ylabels");
categories.forEach(function(cat) {
var yCenter = yScale(cat) + yScale.bandwidth() / 2 + margin.top;
var labelDiv = document.createElement("div");
labelDiv.className = "aji-ai-startup-funding-wrap-ylabel";
labelDiv.style.cssText = [
"position:absolute",
"top:" + yCenter + "px",
"right:0",
"width:130px",
"font-size:11px",
"color:#a7a9be",
"text-align:right",
"line-height:1.3",
"word-break:keep-all",
"overflow-wrap:break-word",
"transform:translateY(-50%)"
].join(";");
labelDiv.textContent = cat;
ylabelsDiv.appendChild(labelDiv);
});
}
コンテナのHTMLは以下の構造で、position:relative が必須。
<div class="aji-ai-startup-funding-wrap">
<div style="position:relative;">
<div id="bubble-ylabels" style="position:absolute; left:0; top:0; pointer-events:none;"></div>
<div id="bubble-chart"></div>
</div>
</div>
Step 6: スマホ対応のタッチイベントと下部固定パネル
スマホではY軸ラベルを非表示にし、バブルをタップしたときに画面下部固定パネルで詳細を表示する。
// スマホタッチイベント(stopPropagation必須)
bubbles.on("touchstart", function(event, d) {
event.preventDefault();
event.stopPropagation(); // 省略するとdocumentのタッチが即発火してパネルが閉じる
var html = "<strong>" + d.name + "</strong><br>"
+ "設立: " + d.founded + "年<br>"
+ "累計調達: 約" + Math.round(d.totalFunding / 1e8) + "億円<br>"
+ "領域: " + d.category + "<br>"
+ "直近R: " + d.latestRound + " (" + d.latestRoundYear + ")";
document.getElementById("panel-content").innerHTML = html;
document.getElementById("mobile-panel").style.display = "block";
});
// パネル外タップで閉じる
document.addEventListener("touchstart", function(event) {
var panel = document.getElementById("mobile-panel");
if (panel) {
if (panel.style.display === "block") {
if (!panel.contains(event.target)) {
panel.style.display = "none";
}
}
}
});
つまずきポイント: e.stopPropagation() を省略すると「バブルタップ→パネル表示→documentタッチ発火→パネル閉じる」が瞬時に起きる。パネルが開いて即閉じる現象が起きた場合はまずここを疑う。
つまずきポイントまとめ
-
D3が動かない(WordPress):
<script src="CDN">はWordPressに無視される → 動的ロードに変更 -
&&の後でスクリプトが止まる: WordPressが&&を&&に変換 → ネストifまたは三項演算子に置換 - 日本のバーが見えない(国際比較): 線形スケールでは80倍差が表現できない → 対数スケール + domain最小値を1に設定
- バブルのY軸ラベルがスマホで切れる: SVG textは自動改行不可 → HTML div + position:absolute + word-break:keep-all
-
タッチパネルが即閉じる:
e.stopPropagation()が抜けている → タッチイベントに必ず追加 -
scaleBandでバブルがずれる: ドメインとアクセサのキーが不一致 →
yScale.domainとbubbles.attr("cy")のキーを揃える -
ハッチングが表示されない:
defsをsvg.append("defs")で先に定義してからfill="url(#hatch-estimate)"を指定する
まとめ
- D3.js v7のSVG描画でWordPressのChart.js競合問題を回避できた
-
&&禁止・CDNタグ禁止・wp:htmlラッパーの3点がWordPressでのD3実装必須事項 - 推計値と確定値の区別はデータの
isEstimateフラグ + SVGハッチングパターンで実装する - 80倍以上の差があるデータ比較には対数スケール。
domainの最小値に注意 - 日本語10文字超のY軸ラベルはHTML div + position:absolute で実装する
実装の動作確認はAIスタートアップ資金調達マップ(AI Japan Index)で行える。類似のデータビジュアライゼーションツールはAI Japan Indexのツール一覧にまとまっている。
