1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【D3.js】日本AIスタートアップ資金調達データ(5チャート)をWordPressで可視化する — &&禁止・script src禁止・バブルチャートのY軸HTML対応まで

1
Posted at

ai-startup-funding.jpg

この記事でできること

  • 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のコンテンツフィルターは &&&#038;&#038; に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が &&&#038;&#038; に変換 → ネストifまたは三項演算子に置換
  • 日本のバーが見えない(国際比較): 線形スケールでは80倍差が表現できない → 対数スケール + domain最小値を1に設定
  • バブルのY軸ラベルがスマホで切れる: SVG textは自動改行不可 → HTML div + position:absolute + word-break:keep-all
  • タッチパネルが即閉じる: e.stopPropagation() が抜けている → タッチイベントに必ず追加
  • scaleBandでバブルがずれる: ドメインとアクセサのキーが不一致 → yScale.domainbubbles.attr("cy") のキーを揃える
  • ハッチングが表示されない: defssvg.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のツール一覧にまとまっている。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?