この記事でできること
- WordPressでD3.jsのインタラクティブなチャートを動かす方法がわかる
-
&&演算子がWordPressで使えない理由と回避策がわかる - D3のタイムスケール(時系列)の基本的な使い方がわかる
- スマホ横スクロール年表の実装パターンがわかる
- 実際に動作するツールはAI規制・法律タイムライン 2026で確認できる
環境・前提
- ブラウザ: Chrome/Firefox/Safari(モダンブラウザ)
- D3.js: v7.9.0(CDN経由で動的ロード)
- CMS: WordPress 6.x + SWELLテーマ
- 言語: Vanilla JavaScript(ES2020相当)
- ビルドツール: なし(全コードをWordPress投稿コンテンツに直接埋め込む形式)
完成形
5か国(日本・EU・米国・中国・韓国)と国際機関のAI規制イベント42件を横スクロールの年表チャートで表示する。各マーカーをクリック/タップすると、そのイベントの概要と出典が表示される。
→ https://gozen-ai.com/tools/ai-regulation-timeline/
手順
Step 1: D3.jsをWordPressで読み込む
WordPressの投稿コンテンツ内に <script src="cdn.url"> と書いても動かない。SWELLテーマ(というかWordPressのコンテンツフィルター)が外部スクリプトタグを無視するからだ。
NG例(WordPressでは動かない):
<script src="https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js"></script>
<script>
// d3 is not defined になる
const svg = d3.select("#chart");
</script>
OK例(動的ロードで回避):
function loadD3AndInit() {
// 既にロード済みならスキップ
if (typeof d3 !== 'undefined') {
initAll();
return;
}
// document.createElementで動的ロード
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js';
script.onload = function() { initAll(); };
document.head.appendChild(script);
}
document.addEventListener('DOMContentLoaded', loadD3AndInit);
document.createElement('script') でscriptタグを作り、document.head.appendChild で動的にheadに追加する。これならSWELLのフィルターをすり抜けられる。
つまずきポイント: DOMContentLoaded の前に loadD3AndInit() を呼ぶと、まだ描画対象のdivが存在しないため d3.select("#chart") が null を返す。必ずDOM構築後に実行する。
Step 2: &&演算子を使わない
WordPressのコンテンツフィルターが && を && というHTMLエンティティに自動変換する。これがJavaScriptとして実行されると && は文法エラーになり、スクリプト全体が止まる。
NG例(WordPressが変換してJSエラーになる):
if (a && b) {
doSomething();
}
OK例(三項演算子でネスト):
// 三項演算子で書く
const result = a ? (b ? doSomething() : null) : null;
// またはネストifで書く
if (a) {
if (b) {
doSomething();
}
}
つまずきポイント: && を || に混同しないこと。|| は変換されない。また &&= の複合代入演算子も変換対象になるので注意。
Step 3: CSSをツール内にスコープする
WordPressにはもともとSWELLテーマのCSSが大量に読み込まれている。グローバルセレクタ(body {} や h2 {} 等)を書くとSWELLのスタイルを上書きしてサイト全体のレイアウトが崩れる。
NG例(SWELLを破壊する):
* { box-sizing: border-box; }
body { background: #0f0e17; }
h2 { margin: 0; color: white; }
OK例(ラッパーでスコープ):
.aji-art-wrap * { box-sizing: border-box; }
.aji-art-wrap h2 { margin: 0; color: #e0e0e0; }
ツール全体を <div class="aji-art-wrap"> で囲み、全CSSセレクタに .aji-art-wrap を先頭に付ける。
ポイント: クラス名は他のツールと衝突しないよう aji-{ツールID}- プレフィックスを使うのが実務上の慣習になっている。
Step 4: D3のタイムスケールを設定する
時系列の横軸を作る。D3の scaleTime() は Date オブジェクトを受け取る。
// 日付文字列を Date オブジェクトにパースする
const parseDate = d3.timeParse("%Y-%m");
// NG: new Date("2024-08") はUTC解釈でタイムゾーンズレが起きることがある
// const date = new Date("2024-08");
// OK: d3.timeParse で明示的にパース
const date = parseDate("2024-08"); // Mon Aug 01 2024 00:00:00 (ローカルタイム)
// スケールの設定
const xScale = d3.scaleTime()
.domain([parseDate("2016-01"), parseDate("2028-12")])
.range([0, innerWidth]);
つまずきポイント: new Date("2024-08") は環境によって UTC として解釈されるため、日本(UTC+9)では 2024年7月31日 09:00:00 になる。表示は問題ないように見えても、マーカーの位置が微妙にズレる。d3.timeParse を使えばローカルタイムで確実にパースされる。
Step 5: スマホ横スクロールに対応する
年表の横軸が2016〜2028の12年分なので、スマホ画面(375px)には収まらない。横スクロールで対応する。
/* スクロールコンテナ */
.aji-art-timeline-outer {
overflow-x: auto;
-webkit-overflow-scrolling: touch; /* iOS慣性スクロール */
width: 100%;
}
/* SVGの最小幅を保証 */
.aji-art-timeline-svg-wrap {
min-width: 640px;
}
スマホでは年ラベルが多すぎると重なる。最初・中間・最後の3点だけ表示する。
const isMobile = window.innerWidth <= 767;
const years = d3.range(2016, 2029); // [2016, 2017, ..., 2028]
const tickValues = isMobile
? [years[0], years[Math.floor((years.length - 1) / 2)], years[years.length - 1]]
: years;
const xAxis = d3.axisBottom(xScale)
.tickValues(tickValues.map(y => new Date(y, 0)));
つまずきポイント: 横スクロールコンテナのスタイルを親要素に当てること。SVG自体に overflow を設定しても横スクロールは効かない。
Step 6: タッチイベントでパネルを開閉する
スマホではhoverが使えないので、マーカーをタップすると画面下部に詳細パネルが出る仕組みにした。
marker.on('touchstart', function(e) {
e.preventDefault();
e.stopPropagation(); // ← これが超重要
showPanel(event);
});
// パネルの外をタップしたら閉じる
document.addEventListener('touchstart', function() {
hidePanel();
});
つまずきポイント: e.stopPropagation() を書かないと、マーカーのタッチイベントが document まで伝播して hidePanel() が即座に呼ばれる。パネルが開いた瞬間に閉じるという謎バグになる。e.preventDefault() と e.stopPropagation() は必ずセットで書く。
つまずきポイントまとめ
-
&&がJSエラーになる: WordPressのコンテンツフィルターが&&に変換する。三項演算子かネストifで回避 -
CDN script タグが動かない: SWELL/WordPressはコンテンツ内の
<script src>を無視する。document.createElementで動的ロードに切り替える -
タイムゾーンズレ:
new Date("2024-08")はUTC解釈になる。d3.timeParse("%Y-%m")を使う -
タッチしたらパネルが即閉じる:
e.stopPropagation()の書き忘れ。e.preventDefault()とセットで必ず書く -
グローバルCSS:
body {}やh2 {}を書くとSWELLのテーマが壊れる。ラッパークラスでスコープする
まとめ
- WordPressでD3.jsを動かすには「CDN動的ロード」「
&&回避」「CSSスコープ」の3点が必須 - D3の時系列データは
d3.timeParse()で明示的にパースする - 横スクロール年表はコンテナに
overflow-x: autoを設定し、SVGにmin-widthを指定する - タッチイベントの
e.stopPropagation()は省略しない - スマホの年ラベル間引きは「最初・中間・最後の3点」パターンが実用的
実装したツールはこちら → AI規制・法律タイムライン 2026
AI Japan Indexの全ツール一覧 → https://gozen-ai.com/
