AIエージェントのデータダッシュボードを8種類のD3.jsチャートで作ってみた話
AIエージェントの普及率や市場規模データをまとめたダッシュボードを作った。
エリアチャート、ロリポップ、ステップ、ワッフル、ダンベル、対数棒グラフ…と8種類のチャートをWordPressの1記事内に全部突っ込む作業だった。
「D3.jsは聞いたことある」「WordPressでチャートを出したい」というレベルの方に向けて、失敗したところを中心に書く。
完成したもの: AIエージェント普及率ダッシュボード
この記事でできること
- D3.jsをWordPressで動かす基本パターンが理解できる
- 「どんなデータにどんなチャートを使うか」の選び方がわかる
- SVGとHTML divの使い分けルールが整理できる
- スマホ対応(タップで詳細表示)の実装パターンがわかる
- 実際に動作するダッシュボードはAIエージェント普及率ダッシュボードで確認できる
環境・前提
- ブラウザ: モダンブラウザ(Chrome, Firefox, Edge最新版)
- D3.js: v7(CDNから動的ロード)
- WordPress: 6系 + SWELLテーマ
- JavaScript: Vanilla JS(ES5準拠、
const/let使わない) - 外部依存: D3.js以外なし
完成形
8つのチャートを1ページに並べたダッシュボード。PCでは2カラムレイアウト、スマホでは縦積み。全チャートがタップ/ホバーで詳細を表示する。
こちらから実物を確認できる。
作ってみて思ったのは、「8種類のチャートを1つの記事内に入れる」という設計よりも、「WordPressで動かす」ための制約対応に時間がかかったということだ。その話を中心に書く。
手順
Step 1: WordPressでD3.jsを動かす
最初にやることは「D3.jsを動かす」の1点に集中する。
NG例(SWELLでは動かない):
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script>
var svg = d3.select('body')...
</script>
SWELLテーマはカスタムHTMLブロック内の外部scriptタグを無視する。エラーも出ない。ただ読み込まれない。
OK例(動的ロードパターン):
function loadD3(callback) {
if (typeof d3 !== 'undefined') {
callback();
return;
}
var s = document.createElement('script');
s.src = 'https://cdn.jsdelivr.net/npm/d3@7';
s.onload = callback;
document.head.appendChild(s);
}
loadD3(function() {
// ここでD3.jsを使う
initAllCharts();
});
typeof d3 で既にロード済みかを確認してから追加する。同じページに複数チャートブロックがある場合でも、D3.jsを二重ロードしない。
補足: callback ではなく onload で初期化を呼ぶことで、D3.jsが確実に使える状態になってから処理が始まる。
Step 2: && 問題に先回り対応する
つまずきポイント: WordPressは && をHTMLエンティティ(&&)に変換する。JSの構文として壊れてスクリプト全体が動かなくなる。
// NG: WordPressが壊す
if (a > 0 && b > 0) { ... }
// OK: 三項演算子で回避
var result = (a > 0) ? ((b > 0) ? doSomething() : null) : null;
// OK: ネストifで回避
if (a > 0) {
if (b > 0) {
doSomething();
}
}
新しくチャートを書く前に「&& を一切使わない」というルールを頭に入れておくと、後から直す手間が省ける。
Step 3: CSSをスコープで囲む
ポイント: グローバルセレクタ(*{} body{} h1{} 等)はSWELLテーマを壊す。必ずツール固有のクラスでスコープを切る。
/* NG: グローバル */
.chart-container { ... }
h3 { ... }
/* OK: スコープ付き */
.aji-ai-agent-adoption-wrap .chart-container { ... }
.aji-ai-agent-adoption-wrap h3 { ... }
全てのCSSセレクタの先頭に .aji-{ツールID}-wrap を付ける。これだけで「テーマが崩れた」系の問題は99%防げる。
Step 4: SVGとHTML divの担当を決める
チャートを書く前に「何をSVGで描いて、何をHTMLで描くか」を決める。これを後から変えると全面書き直しになる。
チャート本体(バー・線・丸・エリア) → SVG
軸の線と目盛り数値 → SVG
バー右横の数値(PCのみ) → SVG
業界名・目的名等の長い日本語ラベル → HTML div(必須)
注記・出典テキスト → HTML div(必須)
凡例 → HTML div(必須)
推定値バッジ → HTML div
なぜラベルをHTMLにするか: SVGの <text> は自動改行しない。「テクノロジー・メディア・通信」のような長いラベルがスマホで確実に見切れる。HTML divはCSSのword-wrapで自動改行されるのでスマホでも安全。
Step 5: スマホのタッチイベントを実装する
スマホではマウス追従型のツールチップが使えない。代わりに画面下部に固定パネルを出す方式にする。
// PC: hoverで表示
element.addEventListener('mouseover', function(e) {
showTooltip(e, data);
});
// スマホ: タップで下部固定パネル
element.addEventListener('touchstart', function(e) {
e.preventDefault();
e.stopPropagation(); // これがないとパネルが開いて即閉じる
showMobilePanel(data);
});
つまずきポイント: e.stopPropagation() を書かないと、タッチイベントがdocumentまで伝わって「パネル外タップ」と誤判定される。開いた瞬間に閉じる謎のバグが起きる。必ず e.preventDefault() と e.stopPropagation() をセットで書く。
Step 6: スマホでは数値ラベルを非表示にする
バー横の数値ラベルはPC専用にする。スマホで表示すると他の要素と重なる。
var mobile = window.innerWidth <= 767;
// 数値ラベルはPCのみ
if (!mobile) {
g.append('text')
.attr('x', xScale(d.value) + 8)
.attr('y', barY)
.attr('fill', '#e0e0e0')
.attr('font-size', '12px')
.text(d.value + '%');
}
スマホでは数値ラベルを出さない代わりに、タップで下部パネルに詳細を表示する。「チャートを見てタップで詳細」という動線にする。
つまずきポイントまとめ
-
外部scriptタグが効かない: SWELLはカスタムHTMLブロック内の外部scriptタグを無視する。動的ロードパターン(
createElement('script'))を使う -
&&がJSを壊す: WordPressが&&をHTMLエンティティに変換する。三項演算子かネストifに書き換える -
グローバルCSSがテーマを崩す:
.aji-{ツールID}-wrapスコープを全セレクタに付ける -
ラベルがスマホで見切れる: 長い日本語ラベルはSVG
<text>ではなくHTML divで実装する -
パネルが開いて即閉じる:
touchstartにe.stopPropagation()を忘れている。e.preventDefault()とセットで必ず書く
まとめ
- D3.jsをWordPressで動かす際のポイントは「動的ロード・
&&回避・CSSスコープ・SVG/HTML分担・スマホタッチ対応」の5点 - チャートの種類選びより「WordPressで動かす制約対応」に時間がかかる
- 8種類のチャートを作って分かったのは「データに合ったチャート選び」が表現力の9割を決めるということ
- 対数スケールを使うときは「なぜ等間隔でないか」の注記を必ずHTML divで出す
完成したダッシュボードと関連ツール一覧は以下から。
AIエージェント普及率ダッシュボード
AI Japan Index — 全ツール一覧
