ClaudeとJavaScriptで作る男脳・女脳診断テスト - 実装解説
はじめに
書籍「話を聞かない男、地図が読めない女」に掲載されている診断テストを、JavaScriptとWordPressを使ってWebアプリケーション化しました。30問の質問に答えるだけで、思考パターンを分析できる診断システムです。
この記事で分かること
- HTML/CSS/JavaScriptによる診断テストの実装方法
- WordPressのAJAX機能を使った利用回数カウンター機能
- レスポンシブ対応の診断UIの作り方
- スコア計算ロジックとリアルタイム結果表示
この記事の対象者
- JavaScript初心者から中級者の方
- WordPressでインタラクティブなコンテンツを作りたい方
- 診断系Webアプリに興味があるエンジニア
動作環境・使用技術
- HTML5 / CSS3 / Vanilla JavaScript
- WordPress 6.8.1
- Lightning テーマ 15.29.11
- レスポンシブデザイン対応
自己紹介
ホネグミ代表、応用情報技術者の資格を持つエンジニア×マーケターです。これまでIT系の会社役員を4年、独立して4年目になります。クライアントワークでは「こうしたい」を技術で形にすることを専門としていますが、最近は個人開発でAIを「無駄に」使った社会風刺的なサービスも作っています。
完成品・デモ
実装した機能:
- 30問の選択式質問
- リアルタイム進捗表示
- 自動スコア計算
- 詳細結果解説
- 累計利用回数カウンター
- レスポンシブ対応
技術構成
フロントエンド:
- HTML5(セマンティックなマークアップ)
- CSS3(フレックスボックス、グラデーション)
- Vanilla JavaScript(軽量実装)
バックエンド:
- WordPress(CMS基盤)
- PHP(AJAX処理)
- WordPress AJAX API(利用回数管理)
実装手順
1. 基本HTML構造
診断テストの基本構造です:
<div class="brain-test-container">
<div id="progress-bar" class="progress-bar"></div>
<h2>男脳・女脳 診断テスト</h2>
<!-- 累計使用回数表示 -->
<div class="usage-stats">
<p class="total-usage">累計診断回数: <span id="total-count">読み込み中...</span>回</p>
</div>
<!-- 性別選択 -->
<div class="gender-select question">
<h3>性別を選んでください</h3>
<label><input type="radio" name="gender" value="male" required> 男性</label>
<label><input type="radio" name="gender" value="female" required> 女性</label>
</div>
<!-- 質問コンテナ -->
<div id="questions-container">
<!-- 30問の質問がここに動的に生成される -->
</div>
<!-- 結果表示エリア -->
<div id="score-display" class="score-display">
<h3>テスト結果</h3>
<p>合計点: <span id="score">0</span>点</p>
<div id="score-explanation">
<!-- 詳細解説がここに表示される -->
</div>
</div>
<button onclick="calculateScore()" class="score-button">採点する</button>
</div>
2. スタイリング
レスポンシブ対応とユーザビリティを重視したCSS:
.brain-test-container {
max-width: 800px;
margin: 0 auto;
font-family: sans-serif;
padding: 15px;
}
/* 累計使用回数表示のスタイル */
.usage-stats {
text-align: center;
margin: 15px 0 25px 0;
padding: 12px;
background: linear-gradient(135deg, #f3e7ff, #e8d5ff);
border-radius: 8px;
border: 2px solid #9b59b6;
box-shadow: 0 2px 8px rgba(155, 89, 182, 0.1);
}
.question {
margin-bottom: 20px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
background: #fff;
}
.question label {
display: block;
margin: 10px 0;
padding: 10px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s ease;
}
.question label:hover {
background: #f5f5f5;
}
/* 固定ボタンのスタイル */
.score-button {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
padding: 15px 30px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 25px;
font-size: 16px;
cursor: pointer;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
z-index: 1000;
transition: background-color 0.3s ease;
}
.score-button:hover {
background-color: #45a049;
}
/* プログレスバー */
.progress-bar {
position: fixed;
top: 0;
left: 0;
width: 0%;
height: 4px;
background: #4CAF50;
transition: width 0.3s ease;
z-index: 1000;
}
/* レスポンシブ対応 */
@media (max-width: 768px) {
.brain-test-container {
padding: 10px;
}
.score-button {
padding: 12px 24px;
font-size: 14px;
}
}
3. JavaScriptロジック
スコア計算と結果表示の核となるJavaScript:
// ページ読み込み時に累計カウント表示
document.addEventListener('DOMContentLoaded', function() {
loadCurrentCount();
generateQuestions();
updateProgressBar();
});
// 質問を動的に生成
function generateQuestions() {
const questions = [
{
id: 1,
text: "地図を読むのが得意ですか?",
options: [
{ value: "a", text: "とても得意" },
{ value: "b", text: "普通" },
{ value: "c", text: "苦手" }
]
},
// 実際は30問の質問データ
// ...
];
const container = document.getElementById('questions-container');
questions.forEach(question => {
const questionDiv = createQuestionElement(question);
container.appendChild(questionDiv);
});
}
// 質問要素を作成
function createQuestionElement(question) {
const div = document.createElement('div');
div.className = 'question';
div.innerHTML = `
<h3>Q${question.id}. ${question.text}</h3>
${question.options.map(option => `
<label>
<input type="radio" name="q${question.id}" value="${option.value}" onchange="updateProgressBar()">
${option.text}
</label>
`).join('')}
`;
return div;
}
// プログレスバーを更新
function updateProgressBar() {
const totalQuestions = 30;
let answeredQuestions = 0;
for(let i = 1; i <= totalQuestions; i++) {
const radios = document.getElementsByName('q' + i);
for(let j = 0; j < radios.length; j++) {
if(radios[j].checked) {
answeredQuestions++;
break;
}
}
}
const progress = (answeredQuestions / totalQuestions) * 100;
document.getElementById('progress-bar').style.width = progress + '%';
}
// 累計カウント数を取得して表示
function loadCurrentCount() {
fetch('/wp-admin/admin-ajax.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'action=brain_test_get_count'
})
.then(response => response.text())
.then(count => {
document.getElementById('total-count').textContent = count;
})
.catch(error => {
console.error('カウント取得エラー:', error);
document.getElementById('total-count').textContent = '0';
});
}
// カウンターを更新する関数
function updateUsageCounter() {
fetch('/wp-admin/admin-ajax.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'action=brain_test_counter'
})
.then(response => response.text())
.then(count => {
document.getElementById('total-count').textContent = count;
// カウント更新時のアニメーション効果
const countElement = document.getElementById('total-count');
countElement.style.transform = 'scale(1.2)';
countElement.style.transition = 'transform 0.3s ease';
setTimeout(() => {
countElement.style.transform = 'scale(1)';
}, 300);
})
.catch(error => {
console.error('カウンター更新エラー:', error);
});
}
// メインのスコア計算関数
function calculateScore() {
let score = 0;
let answeredQuestions = 0;
// 全30問をループで処理
for(let i = 1; i <= 30; i++) {
const radios = document.getElementsByName('q' + i);
let answered = false;
for(let j = 0; j < radios.length; j++) {
if(radios[j].checked) {
answered = true;
// スコア計算ロジック
switch(radios[j].value) {
case 'a':
score += 10;
break;
case 'b':
score += 5;
break;
case 'c':
score -= 5;
break;
}
}
}
if(answered) {
answeredQuestions++;
}
}
// 全問回答チェック
if(answeredQuestions < 30) {
alert('すべての質問に回答してください。');
return;
}
// 採点成功時にカウンターを更新
updateUsageCounter();
// 結果表示
displayResults(score, answeredQuestions);
}
// 結果表示関数
function displayResults(score, answeredQuestions) {
const display = document.getElementById('score-display');
const scoreSpan = document.getElementById('score');
const explanation = document.getElementById('score-explanation');
// 脳タイプの判定ロジック
let typeText = "";
let detailText = "";
if (score < 150) {
typeText = "あなたは【男脳タイプ】です。";
detailText = "論理的思考が得意で、空間認識能力に長けています。問題解決において体系的なアプローチを取る傾向があります。";
} else if (score <= 180) {
typeText = "あなたは【バランス型】です。";
detailText = "男脳・女脳の特徴をバランス良く持っています。状況に応じて柔軟な思考パターンを使い分けることができます。";
} else {
typeText = "あなたは【女脳タイプ】です。";
detailText = "感情豊かで直感力に優れています。コミュニケーション能力が高く、複数のことを同時に処理するのが得意です。";
}
display.style.display = 'block';
scoreSpan.textContent = score;
explanation.innerHTML = `
<h4>${typeText}</h4>
<p>${detailText}</p>
`;
// 結果表示位置にスムーススクロール
display.scrollIntoView({ behavior: 'smooth' });
}
4. WordPress側のPHP実装
functions.phpに以下を追加して、AJAX通信を処理します:
// AJAX ハンドラー関数 - カウンター増加
function brain_test_counter_increment() {
$count = get_option('brain_test_usage_count', 0);
$count++;
update_option('brain_test_usage_count', $count);
wp_die($count);
}
// AJAX ハンドラー関数 - カウンター取得
function brain_test_counter_get() {
$count = get_option('brain_test_usage_count', 0);
wp_die($count);
}
// AJAX アクション登録(ログインユーザー用)
add_action('wp_ajax_brain_test_counter', 'brain_test_counter_increment');
add_action('wp_ajax_brain_test_get_count', 'brain_test_counter_get');
// AJAX アクション登録(非ログインユーザー用)
add_action('wp_ajax_nopriv_brain_test_counter', 'brain_test_counter_increment');
add_action('wp_ajax_nopriv_brain_test_get_count', 'brain_test_counter_get');
工夫したポイント
1. ユーザーエクスペリエンスの最適化
固定ボタンによる操作性向上:
30問すべて回答後、画面をスクロールすることなく採点できるよう、採点ボタンを画面下部に固定配置しました。
視覚的フィードバック:
- リアルタイムプログレスバーによる進捗表示
- 利用回数更新時のアニメーション効果
- ホバー効果による直感的な操作感
2. WordPress AJAX連携
データ永続化の実装:
WordPressのwp_optionsテーブルを使用してカウンターデータを永続化しました。テーマ変更やプラグイン更新の影響を受けない安全な実装です。
エラーハンドリング:
ネットワークエラーやサーバーエラーが発生した場合でも、診断機能自体は継続して利用できるよう設計しています。
3. パフォーマンス最適化
Vanilla JavaScript採用:
jQueryを使わずにVanilla JavaScriptで実装し、軽量化を図りました。外部ライブラリ依存を避けることで、読み込み速度も向上しています。
非同期処理:
カウンター更新処理を非同期で実行し、ユーザーの診断体験を阻害しないよう配慮しました。
苦労した点
レスポンシブ対応
特にスマートフォンでの操作性確保が課題でした。固定ボタンの配置やタップ領域の最適化に工夫が必要でした。
スコア計算ロジックの実装
書籍の診断基準を正確にWebアプリに反映するため、スコア計算アルゴリズムの調整に時間をかけました。
WordPressとの連携
AJAX通信での適切なエラーハンドリングと、セキュリティを考慮したPHP実装に注意が必要でした。
まとめ
書籍の診断テストをWeb化することで、紙ベースでは実現できなかった以下の価値を提供できました:
- 自動採点機能による即座の結果表示
- リアルタイムカウンターによる社会的証明の可視化
- レスポンシブ対応による多デバイス対応
- 進捗表示によるユーザビリティ向上
単純な機能ながら、ユーザビリティとエンターテイメント性を両立させた実装となりました。
WordPressとJavaScriptの連携パターンとしても参考になる事例だと思います。診断系コンテンツやインタラクティブなWebアプリケーション開発の参考になれば幸いです。
参考リンク
参考書籍
📚 話を聞かない男、地図が読めない女
アラン・ピーズ (著), バーバラ・ピーズ (著), 藤井 留美 (翻訳)
🛒 Amazonで詳細を見る
この書籍の診断テストを元に今回のWebアプリケーションを開発しました。男女の脳の違いについて科学的な観点から解説されており、非常に興味深い内容です。
Discussion