九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、このブログではできるだけプログラミングの楽しさを多くの人に知ってもらいたいという気持ちで公開している記事も多くあるのですが、どうしても扱っているのがプログラミングということで内容が複雑になってしまったり、テーマが業務的な内容になってしまいどうしたもんかな、なんて考えていました。
そして、私の経験上(ピアノや英語)でそうだったんですが、やはり学ぶにはできるだけ好きなものとくっつけると長続きますし、覚えも早かったので、今回は「仕事に直結しないけど、学ぶにはいいだろう」という内容で記事を書いてみることにします。
ということで、開発する内容はズバリ「3択クイズ」です。
3択クイズはテレビ番組なんかでもよく見るクイズ形式なので、題材としてはきっといいのではないかと考えました。
もちろん、初心者にもできるだけ分かりやすくしているので、ぜひ楽しみながらプログラミングに触れてみてくださいね。(最後に教材ソースコード2つをダウンロードできます!)
※ 環境: Google Chrome 70, Vue 2
目次
やりたいこと
- 1問ずつクイズを表示
- 解答は3択
- 最後に結果を表示する
レイアウトをつくる
ではまずは3択クイズのレイアウトを作成します。
今回はCSSにbootstrap
、JavaScriptにVue.js
を使いますが、インターネット上から呼び出すのでインストールは必要ありません。
<html> <head> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet"> <style> body { padding: 50px; } </style> </head> <body> <div id="app" class="row"> <div class="offset-3 col-6"> <div class="card"> <div class="card-body"> <p class="badge badge-dark">第 1 問</p> <br> <h4 class="card-title">囲碁の石の大きさはどっちが大きい?</h4> <hr> <button type="button" class="btn btn-primary btn-lg btn-block text-left">1. 白い石</button> <button type="button" class="btn btn-primary btn-lg btn-block text-left">2. 黒い石</button> <button type="button" class="btn btn-primary btn-lg btn-block text-left">3. どちらも同じ</button> </div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js"></script> <script> new Vue({ el: '#app' }) </script> </body> </html>
これを実行すると、次のようになります。
ではこのレイアウトを使ってJavaScript(Vue.js)部分を作っていきましょう。
JavaScript部分をつくる
クイズを表示する部分をつくる
では、クイズを表示する部分です。
まずは、questions
という名前の変数をdata
に登録しましょう。
new Vue({ el: '#app', data: { questions: [ { question: '囲碁の石の大きさはどっちが大きい?', answers: [ '白い石', '黒い石', 'どちらも同じ', ], answer: 1 }, { question: 'コンビニチェーンのセブンイレブン。アルファベット表記で1文字だけ小文字なのは?', answers: [ 'N', 'L', 'V', ], answer: 0 }, { question: 'カラオケは「空○○」略です。空欄に入る言葉は何?', answers: [ 'OK', 'オーケストラ', 'おけら', ], answer: 1 } ] } })
questions
は配列で、その中身はクイズの文章や選択肢、答えがオブジェクトで格納されています。
そして、現在のクイズ番号を保持するquestionIndex
も追加します。
初期値はもちろん0
です。
data: { questionIndex: 0, // 省略 }
次に現在のクイズ内容を自動的に取得できる疑似変数currentQuestion
をcomputed
につくります。
computed: { currentQuestion: function() { return this.questions[this.questionIndex]; } }
これで、レイアウトの中でcurrentQuestion
とすれば現在のクイズ内容を取得することができます。
では、これらの変数をつかって実際に表示してみましょう。
まずは第 ○ 問の部分です。
<p class="badge badge-dark">第 {{ (questionIndex+1) }} 問</p>
プログラム上では配列の番号は0から始まるのでquestionIndex
の初期値は0
にしました。
しかし、このままだと “第 0 問” になってしまうので、1
を足した結果を表示するようにしています。
そして質問が表示される部分です。
先ほどつくった疑似変数currentQuestion
から質問部分を取得しています。
<h4 class="card-title">{{ currentQuestion.question }}</h4>
続いて3択の部分です。
ここは表示すべきデータが3つあるので、ループを使って一気に表示するようにしましょう。
<button type="button" class="btn btn-primary btn-lg btn-block text-left" v-for="(answer,index) in currentQuestion.answers">{{ (index+1) }}. {{ answer }}</button>
まず、v-for
の中身ですが、
(answer,index) in currentQuestion.answers
とすることで、currentQuestion.anwers
から1つずつデータを取得することができます。そして、answer
には解答の選択肢(例えば「白い石」など)が格納され、index
は0
から始まる番号です。
実行するとHTMLはこうなります。
<button type="button" class="btn btn-primary btn-lg btn-block text-left">1. 白い石</button> <button type="button" class="btn btn-primary btn-lg btn-block text-left">2. 黒い石</button> <button type="button" class="btn btn-primary btn-lg btn-block text-left">3. どちらも同じ</button>
解答したときの処理をつくる
クイズの表示は完了したので、続いて解答した時(ボタンをクリックした時)のプログラムをつくっていきましょう。
まず全ての解答を保持しておく変数(配列)、answers
を登録します。
data: { answers: [], // 省略 }
先ほどのボタン部分にクリックイベント、@click="answer(index)"
を付けます。
<button type="button" class="btn btn-primary btn-lg btn-block text-left" v-for="(answer,index) in currentQuestion.answers" @click="addAnswer(index)">{{ (index+1) }}. {{ answer }}</button>
そして、addAnswer()
の本体です。
addAnswer: function(index) { this.answers.push(index); if(this.questions.length == this.answers.length) { // クイズが完了したとき } else { this.questionIndex++; } }
まず答えていく度にanswers
の中身がひとつずつ増えていくことになります。
そして、質問の数と解答の数を比較し、もし同じ(つまりすべて解答済み)の場合とそうでない場合の分岐をif
分で作ります。
もしまだ次の質問が残っている場合は、questionIndex
に1
追加するので、次のクイズが自動的に表示されることになります。
※ ちなみにmethods
とcomputed
の違いは過去記事、Vueの「methods」と「computed」の違いをご覧ください。
クイズが完了したときの処理をつくる
では最後に全ての質問に答えたときの処理を作っていきましょう。
addAnswer: function(index) { this.answers.push(index); if(this.questions.length == this.answers.length) { var correctCount = 0; for(var i in this.answers) { var answer = this.answers[i]; if(answer == this.questions[i].answer) { correctCount++; } } alert(correctCount +'問正解です!'); } else { // 省略 } }
中身としては、解答データanswers
をループさせてひとつずつ解答を取得し、正解データを比較します。そして、この2つが同じ(つまり正解)ならcorrectCount
に1
を追加します。
そして全ての正解チェックが終わったらalert()
を使って「○問正解です!」と表示するようにしています。
おまけ
どうしようか悩んだのですが、初心者向けということで今回は結果の表示を正解数だけにしています。
しかし、せっかくここまで作ったので、結果は「どの問題が正解で、正しい答えは何だったのか?」を表示するようにすることにしました。(そのため、今回教材ソースコードは、ショート・バージョンとフル・バージョンの2つがあります)
では、まずcomputed
にcompleted
というクイズの解答がすべて完了しているかどうかが分かる疑似変数をつくります。
computed: { // 省略 completed: function() { return (this.questions.length == this.answers.length); } }
そして、先ほどつくったaddAnswer()
のalert()
を削除します。
addAnswer: function(index) { this.answers.push(index); if(!this.completed) { this.questionIndex++; } }
さらに、HTMLはこうなります。
<!-- クイズを表示する部分 --> <div class="offset-3 col-6" v-if="!completed"> <div class="card"> <div class="card-body"> <p class="badge badge-dark">第 {{ (questionIndex+1) }} 問</p> <br> <h4 class="card-title">{{ currentQuestion.question }}</h4> <hr> <button type="button" class="btn btn-primary btn-lg btn-block text-left" v-for="(answer,index) in currentQuestion.answers" @click="addAnswer(index)">{{ (index+1) }}. {{ answer }}</button> </div> </div> </div> <!-- 結果表示する部分 --> <div class="offset-3 col-6" v-if="completed"> <div class="card"> <div class="card-body"> <p class="badge badge-dark">結果</p> <div v-for="(question,index) in this.questions"> <h4 class="card-title">{{ question.question }}</h4> <ul> <li v-for="answer in question.answers">{{ answer }}</li> </ul> <span v-if="question.answer == answers[index]">正解 👍</span> <span v-else>不正解 😢<br>正解は「{{ question.answers[question.answer] }}」でした。</span> <hr> </div> </div> </div> </div>
ここで重要なのが、complted
がtrue
かfalse
かで表示内容を切り替えていることです。つまり、解答中はクイズのみ表示し、解答が完了したら結果だけを表示するようにしています。
そして、結果ブロックでは各質問と答えを表示し、正解しているかどうかで表示を切り替えています。
これを実行するとこうなります。
今回は以上です。
お疲れ様でした!
デモページを用意しました
3択クイズを試していただけるよう、デモページを用意しました。
ぜひ楽しんでください♪
教材ソースコードをダウンロードする
今回実際に開発したソースコード一式を以下からダウンロードすることができます。
※ short.html
が短いバージョンで、full.html
が結果表示までするフルバージョンになります。