はじめに
この記事ではJavaScriptの復習の一環として制作したアプリの構成について整理しています。
どういう仕組みで完成したのか、それぞれの役割とは?を振り返っていきます。
初心者向けの記事になりますので、気になる点があればぜひFB頂けますと幸いです。
大まかな構成は以下のとおりとなります。
- HTMLで作った画面に、JavaScriptで動きをつける仕組み
- 回答を点数化して、診断結果を決定する仕組み
- 診断結果の裏側にある、性格特性の考え方
少し長くなりますが、ぜひご一読いただけますと幸いです。
どんなアプリなのか
アプリ作成の時期(10月)にちなんで、「ハロウィン診断アプリ」を制作しました。
10問の質問に回答していくと、5種類のキャラクターに分類されます。
キャラクターはそれぞれハロウィンにちなんだキャラクターで、ランダムに出てくるのではなく、各キャラクターに点数を持たせて分類しています。
参考情報として心理学の知見も活かしていますが、学術的なアプリではなくあくまで遊びの範疇です。
公開ページはこちら
https://nsh0-22.github.io/halloween_js/
また、回答が完了すると診断結果ページに遷移し、SNS(X)に投稿できる機能も実装しました。
振り返り詳細
ここからは実際にこのアプリを通してそれぞれの仕組みや背景を振り返っていきます。
すべてを細かく振り返っていくとかなりの量になるのでピックアップしていきます。
回答を点数化して、診断結果を決定する仕組み
HTMLで画面の構造を用意し、JavaScriptで質問や選択肢などのデータを定義します。
そして、JavaScriptからHTMLの要素を操作することで、用意したデータを画面に表示していきます。
▼HTMLの状態
<div id="question-container">
<p id="question-number">Question Number</p>
<h2 id="question-title">Question Title</h2>
<div id="answer-container">
<button id="answer-a"></button>
<button id="answer-b"></button>
<button id="answer-c"></button>
</div>
<p id="error-message"></p>
<div class="navigation-buttons">
<button id="prev-button" type="button">前へ</button>
<button id="next-button" type="button">次へ</button>
</div>
<div class="result-button">
<button id="result-button" type="button">診断結果を見る</button>
</div>
</div>
<script src="main.js"></script>
JavaScript側のおおまかな流れとしては
-
questionsでルールを定義する -
answersに回答を保存する - 回答に対応する点数を
scoresに加算する - 点数をもとに診断結果を決定する
となります。
const questions = [
{
number: `質問1` ,
title: '友達みんなとハロウィンパーティを開催することに!今年はあなたが主催の年ですが、どんな風に準備を進める?' ,
answers: [
`A. これまでにない形の形式を考えたり、サプライズを用意してワクワクするような回にしよう!` ,
`B. SNSなどで参考情報を集めたりみんなに意見を聞いたりしながら、みんなが楽しめる回にしよう!` ,
`C. 過去の主催者に話を聞いて慎重に準備をして、なるべくこれまでの雰囲気を崩さない回にしよう!`
] ,
scores: [
{ openness: 2 } ,
{ openness: 1 } ,
{ openness: 0, conscientiousness: 1 }
]
} ,
{
number: `質問2` ,
title: 'ハロウィンの仮装で見たことない衣装を着ている人を発見!' ,
answers: [
`A. 自分も着てみたい!次回はこれにしよう!` ,
`B. 何をモチーフにしているんだろう?聞いてみようかな!` ,
`C. みんなそれぞれの個性があっていいなあ!`
] ,
scores: [
{ openness: 2 } ,
{ openness: 2 } ,
{ openness: 0, agreeableness: 1 }
]
} ,
// ・・・・・・中略
{
number: `質問9` ,
title: '来週末は友達の家に泊まってハロウィンのお菓子作り!' ,
answers: [
`A. 自分が担当している持ち物に不備がないか、手土産がいるかなど今からソワソワしちゃう!` ,
`B. みんなでお泊まりとか最高!作るのもいいけど一緒に食べれるお菓子も持っていこう!` ,
`C. 一緒に泊まるメンバーのキャラを想定してみんなが楽しめるような役割分担を頭の中でイメージしちゃう!`
] ,
scores: [
{ neuroticism: 2 } ,
{ extraversion: 1 } ,
{ agreeableness: 1 }
]
} ,
{
number: `質問10` ,
title: '楽しみにしていた仮装パーティーに行く直前、家の玄関の鏡で衣装に穴があいていることに気付いた・・!' ,
answers: [
`A. こういうデザイン!ということにして、みんなに見せて笑い話にしちゃお!` ,
`B. 時間的にギリギリ間に合うから、裁縫して整えてから向かおう!` ,
`C. いつからこの状態だったんだろうとか、他にもほつれているところはないかなとか気になってプチパニック!`
] ,
scores: [
{ extraversion: 1 } ,
{ conscientiousness: 2 } ,
{ neuroticism: 2 }
]
}
];
let currentQuestionIndex = 0;
const answers = [];
const scores = {
openness: 0,
conscientiousness: 0,
extraversion: 0,
agreeableness: 0,
neuroticism: 0
};
🎃質問と点数配分の定義
上記 const questions にて、質問と点数配分を定義しています。
たとえば質問1でCの選択肢を選んだ場合・・
※こちらの配分名(openness等)の詳細については後述します。
{ openness: 0, conscientiousness: 1 }
これは、開放性に0点、誠実性に1点を加算するための設定です。
なお、この段階では点数配分を定義しているだけで、実際の加算処理はまだ行われません。
🎃質問の進捗管理
let currentQuestionIndex = 0;
↑これにより現在表示している質問が何番目なのかを管理します。
なお、インデックスは0から始まるので、現在は1問目を表していることになります。
🎃ユーザーの回答を保存する
const answers = [];
↑ユーザーが実際に選択した回答を保存するための配列です。これにより、1問目でA、2問目でCを答えると
['A', 'C']
と回答が保存されていきます。
const により定義がされている点は同じですが、questions はあらかじめ用意した質問・選択肢・点数配分を保持し、answers はユーザーが実際に選択した回答を保持します。
🎃診断対象ごとの点数管理
最後に、診断結果を決定するための点数を管理します。
const scores = {
openness: 0,
conscientiousness: 0,
extraversion: 0,
agreeableness: 0,
neuroticism: 0
};
5つの性格特性をそれぞれ0点からスタートさせており、回答に応じて点数を加算していき、最終的な点数をもとに診断結果を決めています。(※上記コードは初期値を設定している部分だけなので、厳密にはこのコードだけではまだ点数は加算されていません。)
🎃点数加算の流れ
function calculateScores() {
scores.openness = 0;
scores.conscientiousness = 0;
scores.extraversion = 0;
scores.agreeableness = 0;
scores.neuroticism = 0;
for (let i = 0; i < answers.length; i++) {
const answerScore = questions[i].scores['ABC'.indexOf(answers[i])];
for (const [trait, point] of Object.entries(answerScore)) {
scores[trait] += point;
}
}
console.log(scores);
}
function determineCharacter() {
const maxScore = Math.max(...Object.values(scores));
let character;
if (scores.openness === maxScore) {
character = 'dracula';
} else if (scores.conscientiousness === maxScore) {
character = 'zombie';
} else if (scores.extraversion === maxScore) {
character = 'jackOLantern';
} else if (scores.agreeableness === maxScore) {
character = 'witch';
} else if (scores.neuroticism === maxScore) {
character = 'ghost';
}
return character;
}
-
function calculateScores()
まず、点数のリセットをします。
これによって、関数を実行するたびに前回の点数が残って加算されることを防ぐ意図があります。
(=こうしないと、すごく簡単にいえばずーっと点数が加算されていってしまうようです。)
for (let i = 0; i < answers.length; i++) {
const answerScore = questions[i].scores['ABC'.indexOf(answers[i])];
- 次に、for 文で回答を1問ずつ確認し、選択された回答に対応する点数を取得します。この時
'ABC'.indexOf(answers[i])というindexOf() は、指定した文字が何番目にあるかを返すメソッドによって選択肢と点数を対応させていきます。
for (const [trait, point] of Object.entries(answerScore)) {
scores[trait] += point;
}
- さらに、特性ごとの点数を加算します。たとえば回答が
{ openness: 0, conscientiousness: 1 }だった場合は、openness:0点 conscientiousness:1点を加算します。
※trait = 特性
function determineCharacter() {
const maxScore = Math.max(...Object.values(scores));
- この部分で合計点が最も高い性格特性をもとに、診断キャラクターを決定します。
Object.values(scores)で、5つの性格特性の点数を配列として取り出します。たとえば点数配分が以下の場合は
{
openness: 5,
conscientiousness: 3,
extraversion: 7,
agreeableness: 4,
neuroticism: 1
}
Math.max() によって最高点の 7 が取得されるといった流れです。
🎃キャラクター分類
if (scores.openness === maxScore) {
character = 'dracula';
} else if (scores.conscientiousness === maxScore) {
character = 'zombie';
} else if (scores.extraversion === maxScore) {
character = 'jackOLantern';
} else if (scores.agreeableness === maxScore) {
character = 'witch';
} else if (scores.neuroticism === maxScore) {
character = 'ghost';
}
上記によって各質問に回答した合計得点とそれに割り振られた性格特性ごとのキャラクターを割り振っていきます。
なお、細かい点ですが今回は独自ルールとして、複数の特性が同点で最高点になった場合、このコードでは if文で先に判定される特性が優先されるようにしました。例えば、開放性と外交性がどちらも7点だった場合、開放性の判定が先に行われるため、ドラキュラになります。
診断結果の裏側にある、性格特性の考え方
大まかには上記のような流れで、質問に対しての回答とその保存・管理、各キャラクター分類をしています。
では、この診断対象ごとにある5種類の性格分類について、参考にした情報を解説します。
ビッグファイブという考え方
こちらの記事を参考に、性格特性の5分類を学びました。
ビッグファイブを使った心理テストは世界中で活用されているそうです。今回の診断アプリでは、この性格特性を参考に、イメージキャラクターを選定していきました。詳細はぜひ各種サイトも読んでみてください。
【例】
Openness(開放性)では、知的好奇心の強さや想像力の豊かさ、芸術的感受性、新しいアイデアや行為への親和性を測定します。
【高い人】新しいものやアイデアを生み出すことを好む、想像力豊かで革新的なタイプ
【低い人】物事を決められた通りに進めることを好む、保守的で慎重なタイプ
ここからイメージを落とし込んでいき、診断結果ページのHTMLとJavaScriptに落とし込みました。
<script>
const character = sessionStorage.getItem('character');
const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
const characters = {
dracula: {
name: 'ドラキュラ',
image: 'images/dracula.png',
comment: '血を求めて今日も世界中を飛び回る好奇心旺盛なあなた。夜行性だから晴れた日のお出かけは苦手かも?'
} ,
文章に関しては個人の主観が入りますが、想像力や知的好奇心を表す表現として「血を求めて今日も世界中を飛び回る好奇心旺盛なあなた」とし、ハロウィン要素を入れ込みたいのでドラキュラっぽい一言を添えました。
制作を通しての感想
カリキュラムで学んだ状態だと正直難解でしたが、実際に動かしてみると、JavaScriptのできる範囲が非常に広く、奥深いなと思いました‥!また、シンプルに心理的要素を組み込んだ学習は楽しかったですし、もしかしたら世の中の心理テストの裏側には今回と似たような点数ロジックがあったりするのかなと想像が膨らみました。
