【Vue3でwebアプリをつくろう6】英語のディクテーションをするアプリを作る

この記事では,スマートフォンに対応した語句整序(並べ替え)問題に取り組むwebアプリを開発します。JavaScriptの初学者のために,なるべく丁寧に説明していきます。

ヘッダー

コードの解説に進みます。ヘッダー部の詳細は以前の記事を参考にするとよいでしょう。大事な点は,スマートフォンで文字の大きさを正しく表示するためにビューポートを設定することと,JavaScriptのコードを省略するためのjquery,画面にエフェクトを加えるためのjquery UIを用いている点です。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>fasgram 英文法トレーニングwebアプリ</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif&display=swap" rel="stylesheet">
<link rel="stylesheet" href="./fasgram.css">
</head>

また,今回は外部フォントとしてGoogle Fontsを用いています。使用しているフォントはNoto Serifです。Google Fontsは無料で使える多くのフォントを提供しているので,自分の好みに合ったフォントを探してみると良いでしょう。

body部

<body>
<div id="container">
<header><p class="size-5">fasgram 英文法トレーニングwebアプリ<span class="badge">動作サンプル</span></p></header>
<article>
<audio id="mp3-start" src="./mp3/start.mp3"></audio>
<audio id="mp3-correct" src="./mp3/correct.mp3"></audio>
<audio id="mp3-incorrect" src="./mp3/incorrect.mp3"></audio>
<audio id="mp3-cursor" src="./mp3/cursor.mp3"></audio>
<audio id="mp3-cancel" src="./mp3/cancel.mp3"></audio>
<div id="display"></div>
</article>
<footer></footer>
</div>

ページの内容は<div id="container"></div>で囲まれた部分に記述していきます。これをブロック要素と言います。ブロック要素とは言い換えれば箱のことであって,ここではcontainerという名前の箱を用意して,その中に表示する内容を格納していくことになります。

コードを見ていくと,#containerの中には,<header></header><article></article><footer></footer>という3つの箱が格納されています。footerの箱は今回は使用しないので,空になっています。

headerの箱には画面の一番上に表示されるアプリの名前が格納されています。articleの箱の中にはさらにdisplayという箱が格納されています。ここに実際の英文やボタンなどをあとで記述していきます。

articleの箱にはもう一つ,<audio></audio>が記述されています。これは効果音の音声ファイルを読み込む部分です。ここでは音声は読み込むだけで,実際の再生はあとから行うことになります。

スタート画面

function start_display() {
    const content = '<p class="size-2">'
        + 'ボタンを押して、トレーニングを開始しましょう。'
        + '</p>'
        + '<div class="btn-wrapper">'
        + '<button type="button" id="start-button"'
        + ' class="btn btn-start size-1">並べ替え'
        + '</button>'
        + '</div>';
    $('#display').html(content);
    $('#start-button').on('click', function() {
        $('#mp3-start')[0].play();
        setTimeout(() => {
            sorting();
        },1500);
    });
}

スタート画面については以前の記事で作成したものとまったく同じなので,説明を省きます。

並べ替え問題の表示

async function sorting() {
    //画面の初期化とレイアウトの設定
    const content = '<div style="text-align:right;">'
        + '<button type="button" id="home-btn" class="size-3">'
        + '最初に戻る'
        + '</button></div>'
        + '<p id="instruction" class="size-3">'
        + '適切な英文になるように,ボタンを順番にタップしなさい。'
        + '</p>'
        + '<p id="question-number" class="size-3"></p>'
        + '<p id="en-sentence" class="size-3"></p>'
        + '<p id="comment-box" class="size-3"></p>'
        + '<div id="console" class="btn-wrapper"></div>'
    $('#display').html(content);

ここからが,並べ替え問題の表示部分です。並べ替え問題を実行していく部分を関数sorting()としてまとめています。

ここから登場する,asyncawaitPromise()resolve()は処理を直鎖状に実行していくためのもので,こちらも以前の記事を参照してください。今回のwebアプリのように画面が順番に移り変わりながら進んでいくような場合に必要なものです。

上のbody部で一度用意したブロック要素displayに内容を格納していきます。

流れとしてはいったん文字列contentに書き込みたいhtmlコードを格納し,.html()で書き込みを行います。$('#display').html(content)はjqueryのコードでidがdisplayで指定されたブロック要素にhtmlコードであるcontentを書き込み,画面に表示させる命令です。

コードのid="~"の部分を見ていくと,displayの中にさらにいくつもブロック要素が作られていることが分かります。

home-btnは画面右上の「最初に戻る」ボタンです。

instructionは指示を格納する部分,en-sentenceは英文を格納する部分,comment-boxは正解不正解と日本語訳を格納する部分,consoleは選択肢など画面下のボタンを格納する部分です。

いったんこれらのブロック要素を箱として用意して,次の問題に進む度に箱の中身を入れ替えていく仕組みです。

データの読み込み

問題文のデータをjson形式で用意します。

sorting.json

[
["I don't mind # the job is interesting.",
    "その仕事が面白い限り転勤しても構いません。",
    "being transferred as long as",
    "as","transferred","as","long","being"],
["I should have confirmed the schedule # be late for the party.",
    "パーティーに遅れないようにスケジュールを確認すべきだったのに。",
    "so as not to",
    "to", "so", "not", "as"],
["I'm already # vote in elections.",
    "私はすでに選挙に投票するのに十分な年齢だ。",
    "old enough to",
    "enough", "old", "to"],
["One of the best ways to get to know the local people is # their festival.",
    "地元の人々と知り合いになる最も良い方法の一つは彼らのお祭りに参加することです。",
    "to take part in",
    "to","in","part","take"],
["He #.",
    "彼は宿題を終えたようだ。",
    "seems to have done his homework",
    "homework","to","seems","have","his","done"]
]

json形式と言ってもただの二次元配列として記述しています。こうした形でもjsonファイルとして扱うことができます。今回は問題文を5個用意しました。

    const data = await fetch('./sorting.json');
    const obj = await data.json();
    const obj_json = JSON.stringify(obj); //json文字列に変換
    const texts = JSON.parse(obj_json); //配列に格納

jsonファイルを読み込んで二次元配列に格納します。

texts[0][0] = "I don't mind # the job is interesting."
texts[0][1] = "その仕事が面白い限り転勤しても構いません。"
texts[0][2] = "being transferred as long as"
texts[0][3] = "as"
texts[0][4] = "transferred"
texts[0][5] = "as"
texts[0][6] = "long"
texts[0][7] = "being"
texts[1][0] = "I should have confirmed the schedule # be late for the party."
texts[1][1] = "パーティーに遅れないようにスケジュールを確認すべきだったのに。"
texts[1][2] = "so as not to"
texts[1][3] = "to"
texts[1][4] = "so"
texts[1][5] = "not"
texts[1][6] = "as"

・・・・・・

データは,英文,和訳,解答,選択肢の順に並べます。選択肢は何個でも構いませんし,問題ごとに数が異なっていても対応できます。

    for(let i = 0; i < 1000; i++) {
        let a = Math.floor(Math.random() * texts.length);
        let b = Math.floor(Math.random() * texts.length);
        [texts[a], texts[b]] = [texts[b], texts[a]];
    }

データをシャッフルしてランダムに表示されるようにします。

問題文を繰り返し表示する

    for(let id = 0; id < 3; id++) {

for文で繰り返し処理を行い,問題文を順番に表示していきます。以下では,idは問題番号を表し0から2まで変化していきます。つまり,問題を3つ解いて終了します。

        let en_sentence = texts[id][0];
        const ja_sentence = texts[id][1];
        const answer_phrase = texts[id][2];

文字列en_sentenceに英文を格納し,ja_sentenceに和訳,answer_phraseに解答の文字列を格納します。変数の宣言にletconstを用いています。

letは通常の変数で,あとから値を変更することができますが,constは定数で,あとから値を変更することはできません。値を変更しないものについてはconstを用いて定数であることを明示しておきます。その方が他の人がコードを読むときに仕組みを理解しやすくなります。

for(i = 0; i < texts[id].length - 3; i++) {
  option[i] = texts[id][i + 3];
  blank += '<span id="blank'+i+'" class="bold color-blue"> ___ </span>';
}

選択肢を配列optionに格納します。.lengthは配列の要素数を表すもので,たとえば,texts[0]texts[0][0]からtexts[0][7]まであるので,texts[0].length=8となります。

そして,選択肢の文字列はtexts[0][3]から始まるので,要素数から 3 を引いた回数だけfor文で繰り返し処理を行い,配列に格納してるのです。

option[0] = "as"
option[1] = "transferred"
option[2] = "as"
option[3] = "long"
option[4] = "being"

また,英文の一部を青色の下線に置き換える必要があるので,選択肢の数だけ下線を表示するhtml文字列blankを用意します。

たとえば,texts[0]では選択肢が 5 個あるので,for文の結果,結合された文字列は下線部が5個ある文字列になります。そして,それぞれの下線部を<span></span>で囲みidを設置しておきます。

        $('#question-number').text('No. '+(id+1));
        en_sentence = en_sentence.replace('#',blank);        
        $('#en-sentence').html(en_sentence);
        $('#comment-box').hide();

ここで実際に,問題文が表示されます。#question-numberには問題番号を表示します。

次の行の.replace('#',blank)は英文の中の#を上で用意した文字列blankに置き換えるということです。

en_sentence = "I don't mind # the job is interesting."

.replace("#", blank)  ===>

en_sentence = "I don't mind  ___  ___  ___  ___  ___  the job is interesting."

こうして置き換えた文字列を.html()で書き込み,画面に表示します。また,和訳などを表示するブロック要素comment-boxはいったん.hide()で非表示にして見えないようにしておきます。

選択肢のボタンを表示する

        $('#console').empty();
        for(i = 0; i < option.length; i++) {
            content = '<button type="button"'
                + ' id="option' + i + '"'
                + ' class="btn-white size-3"'
                + ' value="' + i + '">'
                + option[i]
                + '</button>'
            $('#console').append(content);
        }

選択肢のボタンはブロック要素consoleの中に格納していきます。2問目以降では前の問題で表示したボタンが残っているので,いったん,empty()で中身を空にしておきます。

次に,for文で選択肢の数だけ繰り返し処理を行い,ボタンを追加します。' id="option' + i + '"'の部分で,それぞれのボタンにoption0option1option2,・・・というidを割り当てます。このidはあとでボタンを消したり再び表示するために必要です。

また,' value="' + i + '">'の部分で,それぞれのボタンにvalue=0value=1value=2,・・・という値を割り当てます。あとで,ボタンが押されたときにこの値を取得してボタンがどの順番で押されたかを記録していきます。

こうして作られた文字列contentを最後に.append()で追加してボタンを一つずつ表示していきます。

「次に進む」ボタンを表示する

        content = '<button type="button" id="confirm"'
            + ' class="btn btn-confirm size-1">次に進む'
            + '</button>'
        $('#console').append(content);
        $('#confirm').hide(); //いったんボタンを非表示にする

上と同じような方法でブロック要素consoleの最後に「次に進む」ボタンを追加しておきます。しかし,このボタンはいったん.hide()で非表示にして,問題を解き終わったときに表示することになります。

補足ですが,この時点でこのボタンを作っておくのは,あとでPromise()の中でこのボタンにイベントリスナーを設置しているからです。いったんボタンを作っておかないと,イベントリスナーが設置できなくなるので,見えない状態であってもボタン自体を一度作っておく必要があります。

ボタンがクリックされたときの処理

        for(i = 0; i < option.length; i++) {
            $('#option'+i).on('click', function() {

for文で繰り返し処理を行い,それぞれのボタンが押されたときの処理を設定していきます。

.on()はイベントリスナーを設置する命令で,言い換えれば上で作ったボタンoption0option1option2,・・・に一つずつセンサーを取り付けていくようなものです。

'click'はボタンがクリックされたときにセンサーを反応させるという意味で,このときfunction(){}の部分のコードを実行していきます。

次にfunction(){}の中身を見ていきましょう。

$('#mp3-cursor')[0].currentTime = 0;
$('#mp3-cursor')[0].play();

ボタンを押したときのクリック音を再生します。.currentTime=0で音声の再生位置をいったん先頭に戻し,.play()で再生します。ボタンを連続して押したときに,いったん先頭に戻しておかないと次のクリック音がうまく再生されません。

input[seq] = $(this).val();

.val()は上でボタンを設置したときに設定したvalueの値を取得します。

ここで$(this)というセレクタの指定方法が出てきました。これはfunction(){}の前で指定した,'#option'+iの部分を指しています。例えば,最初のボタンであれば$(this)$('#option0')と同じことです。

thisは上手に使えば,コードを省略してその動作が理解しやすくなる利点があります。

こうして取得した値を配列input[]に格納していきます。ボタンの番号は0番目から始まります。たとえば,ボタンを0,3,1の順に押した場合,配列はこのようになります。

input[0] = 0
input[1] = 3
input[2] = 1
input_phrase[seq] = option[input[seq]];

次に配列の値をもとに,選択肢の語句を配列input_phrase[]に格納します。

input_phrase[0] = "as"
input_phrase[1] = "long"
input_phrase[2] = "transferred"
$('#blank'+seq).text(' '+input_phrase[seq]+' ');

そして,英文の中にある下線部を入力された文字列に置き換えます。

I don't mind ___ ___ ___ ___ ___ the job is interesting.

ボタンを押す ==>

I don't mind as ___ ___ ___ ___ the job is interesting.
seq += 1;

seqは上でも用いましたが,これは何番目の下線部に対して処理を行うかを示しています。最初はseq=0ですが,seq=1とすることで次の下線部に対して処理を行っていきます。こうして,処理を行う下線部を一つずつ進めていったり,「一つ戻る」ボタンが押されたときには前に戻って処理をやり直すようにします。

$(this).hide();

最後に,押されたボタンを非表示にして選択肢から取り除きます。

正解不正解の判定

if(seq == option.length) {

全てのボタンが押されたら正解不正解の判定に入ります。

$('#backspace').hide();

「一つ戻る」ボタンを非表示にします。

if(input_phrase.join(' ') == answer_phrase) {

.join()は配列の文字列を結合します。正解の場合は以下のようになります。

input_phrase[0] = "being"
input_phrase[1] = "transferred"
input_phrase[2] = "as"
input_phrase[3] = "long"
input_phrase[4] = "as"

==>

input_phrase.join(' ') = "being transferred as long as"

ここで,正解の文字列はanswer_phrase = "being transferred as long as"なので,二つの文字列が一致し,正解のときの処理に入ります。

content = '<p><span class="color-blue">〇</span> 正解</p>'
    + '<p>' + ja_sentence + '</p>'
$('#comment-box').html(content).show();
$('#confirm').show();
$('#mp3-correct')[0].play();

正解であることを示す文字列と,和訳の文字列ja_sentenceをブロック要素comment-boxに表示します。comment-boxは最初にいったん非表示していたので,.show()で再び表示します。

同様に,上でいったん非表示にしていた「次に進む」ボタンconfirm.show()で表示します。

最後に,正解のときの効果音mp3-correctを再生します。

次に,不正解のときの処理に入ります。

} else {
    content = '<p><span class="color-red">×</span> 不正解</p>'
        + '<p>正しくは <span class="color-red">'
        + answer_phrase
        + '</span></p>'
        + '<p>' + ja_sentence + '</p>'
    $('#comment-box').html(content).show();
    $('#confirm').show();
    $('#mp3-incorrect')[0].play();
}

不正解であることを示す文字列と,正解の文字列answer_phrase,和訳の文字列ja_sentenceを表示します。

あとは,正解のときの処理と同じですが,効果音は不正解の効果音mp3-incorrectを再生します。

「一つ戻る」ボタンの処理

「一つ戻る」ボタンが押されたときの処理を説明します。「一つ戻る」ボタンが押されたときには,前の処理をキャンセルしていきます。

content = '<button type="button"'
    + ' id="backspace"'
    + ' class="btn-white size-3">'
    + '一つ戻る'
    + '</button>'
$('#console').append(content);

ボタンを表示する部分です。idにbackspaceを割り当てています。

$('#backspace').on('click', function() {
    $('#mp3-cancel')[0].currentTime = 0;
    $('#mp3-cancel')[0].play();
    if(seq > 0) {
        seq -= 1;
        content = ' ___ ';
        $('#blank'+seq).text(content);
        $('#option'+input[seq]).show();
    }
});

次にボタンが押されたときの処理です。

まず,キャンセルの効果音mp3-cancelを再生します。

次にif文で2番目以降の下線部のときのみ処理を行うようにします。逆に言えば,選択肢ボタンを押していない最初の状態では「一つ戻る」ボタンを機能しないようにするということです。

seq-=1で下線部を一つ前に戻し,入力された文字列を下線に戻します。

さらに,.show()でいったん非表示にしていたボタンを再び表示します。

「次に進む」ボタンが押されたときの処理

await new Promise((resolve) => {
    $('#confirm').on('click', function(){
        resolve();
    });
});

awaitPromise()resolve()を用いて「次に進む」ボタンが押されるまで次のループに進まないようにします。

まとめ

この記事では,スマートフォンで操作できる整序(並べかえ)問題に取り組めるwebアプリを開発しました。

英文の中にブロック要素を割り当てることで,ボタンが押されたときにその内容を直接英文の中に表示する仕組みを学習しました。また,ボタン操作がキャンセルされたときの処理について学びました。

ここで用いた方法を応用すれば,パズル要素のあるゲームなどを作成することができるでしょう。

最後にコード全体を示します。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>fasgram 英文法トレーニングwebアプリ</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif&display=swap" rel="stylesheet">
<link rel="stylesheet" href="./fasgram.css">
</head>
<body>
<div id="container">
<header><p class="size-5">fasgram 英文法トレーニングwebアプリ<span class="badge">動作サンプル</span></p></header>
<article>
<audio id="mp3-start" src="./mp3/start.mp3"></audio>
<audio id="mp3-correct" src="./mp3/correct.mp3"></audio>
<audio id="mp3-incorrect" src="./mp3/incorrect.mp3"></audio>
<audio id="mp3-cursor" src="./mp3/cursor.mp3"></audio>
<audio id="mp3-cancel" src="./mp3/cancel.mp3"></audio>
<div id="display"></div>
</article>
<footer></footer>
</div>
<script>
//スタート画面
function start_display() {
    const content = '<p class="size-2">'
        + 'ボタンを押して、トレーニングを開始しましょう。'
        + '</p>'
        + '<div class="btn-wrapper">'
        + '<button type="button" id="start-button"'
        + ' class="btn btn-start size-1">並べ替え'
        + '</button>'
        + '</div>';
    $('#display').html(content);
    $('#start-button').on('click', function() {
        $('#mp3-start')[0].play();
        setTimeout(() => {
            sorting();
        },1500);
    });
}
//語句選択問題
async function sorting() {
    //画面の初期化とレイアウトの設定
    const content = '<div style="text-align:right;">'
        + '<button type="button" id="home-btn" class="size-3">'
        + '最初に戻る'
        + '</button></div>'
        + '<p id="instruction" class="size-3">'
        + '適切な英文になるように,ボタンを順番にタップしなさい。'
        + '</p>'
        + '<p id="question-number" class="size-3"></p>'
        + '<p id="en-sentence" class="size-3"></p>'
        + '<p id="comment-box" class="size-3"></p>'
        + '<div id="console" class="btn-wrapper"></div>'
    $('#display').html(content);
    //ホームボタンが押されたら最初に戻る
    $('#home-btn').on('click', function(){
        const result = window.confirm('スタート画面に戻ります。');
        if(result) {
            location.reload();
        }
    });
    //データの読み込み
    const data = await fetch('./sorting.json');
    const obj = await data.json();
    const obj_json = JSON.stringify(obj); //json文字列に変換
    const texts = JSON.parse(obj_json); //配列に格納
    //データをシャッフル
    for(let i = 0; i < 1000; i++) {
        let a = Math.floor(Math.random() * texts.length);
        let b = Math.floor(Math.random() * texts.length);
        [texts[a], texts[b]] = [texts[b], texts[a]];
    }
    //並べ替え問題を順番に表示する
    for(let id = 0; id < 3; id++) {
        //英文と日本文の表示
        let en_sentence = texts[id][0];
        const ja_sentence = texts[id][1];
        const answer_phrase = texts[id][2];
        let option = [];
        let blank = '';
        //選択肢を配列に格納する
        for(i = 0; i < texts[id].length - 3; i++) {
            option[i] = texts[id][i + 3];
            blank += '<span id="blank'+i+'" class="bold color-blue"> ___ </span>';
        }
        //問題番号,英文を表示
        $('#question-number').text('No. '+(id+1));
        en_sentence = en_sentence.replace('#',blank);        
        $('#en-sentence').html(en_sentence);
        $('#comment-box').hide();
        //$('#log').text(answer_phrase);
        //選択肢のボタンを表示
        let content;
        let seq = 0;
        let input = [];
        let input_phrase = [];
        $('#console').empty();
        for(i = 0; i < option.length; i++) {
            content = '<button type="button"'
                + ' id="option' + i + '"'
                + ' class="btn-white size-3"'
                + ' value="' + i + '">'
                + option[i]
                + '</button>'
            $('#console').append(content);
        }
        //次に進むボタンを表示
        content = '<button type="button" id="confirm"'
            + ' class="btn btn-confirm size-1">次に進む'
            + '</button>'
        $('#console').append(content);
        $('#confirm').hide(); //いったんボタンを非表示にする
        //選択肢クリック時の処理
        for(i = 0; i < option.length; i++) {
            $('#option'+i).on('click', function() {
                $('#mp3-cursor')[0].currentTime = 0;
                $('#mp3-cursor')[0].play();
                input[seq] = $(this).val();
                input_phrase[seq] = option[input[seq]];
                $('#blank'+seq).text(' '+input_phrase[seq]+' ');
                seq += 1;
                $(this).hide();
                //正解不正解の判定
                if(seq == option.length) {
                    $('#backspace').hide();
                    if(input_phrase.join(' ') == answer_phrase) {
                        content = '<p><span class="color-blue">〇</span> 正解</p>'
                            + '<p>' + ja_sentence + '</p>'
                        $('#comment-box').html(content).show();
                        $('#confirm').show();
                        $('#mp3-correct')[0].play();
                    } else {
                        content = '<p><span class="color-red">×</span> 不正解</p>'
                            + '<p>正しくは <span class="color-red">'
                            + answer_phrase
                            + '</span></p>'
                            + '<p>' + ja_sentence + '</p>'
                            $('#comment-box').html(content).show();
                            $('#confirm').show();
                        $('#mp3-incorrect')[0].play();
                    }
                }
            });
        }
        //「一つ戻る」ボタンの表示
        content = '<button type="button"'
                + ' id="backspace"'
                + ' class="btn-white size-3">'
                + '一つ戻る'
                + '</button>'
        $('#console').append(content);
        //「一つ戻るボタン」が押された時の処理
        $('#backspace').on('click', function() {
            $('#mp3-cancel')[0].currentTime = 0;
            $('#mp3-cancel')[0].play();
            if(seq > 0) {
                seq -= 1;
                content = ' ___ ';
                $('#blank'+seq).text(content);
                $('#option'+input[seq]).show();
            }
        });
        await new Promise((resolve) => {
            $('#confirm').on('click', function(){
                resolve();
            });
        });
    }
    //全問終了のメッセージ
    await new Promise((resolve) => {
        const content = '<div class="size-1">'
            + '<div class="mb-1">トレーニング終了です。</div>'
            + '<div class="btn-wrapper">'
            + '<button type="button" id="confirm" class="btn btn-confirm size-1">'
            + '最初に戻る'
            + '</button>'
            + '</div>';
        $('#display').empty();
        $('#comment_box').empty();
        $('#display').append(content);
        //OKボタンが押されたらスタート画面へ
        $('#confirm').on('click', function() {
            location.reload();
            resolve();
        });
    });
}
//スタート画面
start_display();
</script>
</body>
</html>

cssのコード全体を示します。今回のコードでは使用していないセレクタも含みます。

/*フォントファミリー*/
body {
    font-family: 'Noto Serif', sans-serif;
}
/*スマートフォン用の基準文字サイズ*/
body {
    font-size: 24px;
}
/*デスクトップ用の基準文字サイズ*/
@media screen and (min-width:680px) {
    body {
        display: flex;
        justify-content: center;
        font-size: 18px;
    }
}
/*コンテナの幅*/
#container {
    max-width: 680px;
}
/*フォントサイズ*/
.size-1 {
    font-size: 1.5rem;
}
.size-2 {
    font-size: 1.25rem;
}
.size-3 {
    font-size: 1.0rem;
}
.size-4 {
    font-size: 0.75rem;
}
.size-5 {
    font-size: 0.5rem;
}
.bold {
    font-weight: 700;
}
/*文字色*/
.color-blue {
    color: #3366CC;
}
.color-red {
    color: #FF6600;
}
/*左マージン*/
.ml-1 {
    margin-left: 10px;
}
/*下マージン*/
.mb-1 {
    margin-bottom: 10px;
}
/*中央に表示するメッセージ*/
.message {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    height: 200px;
    background-color: #e5eeff;
    border-radius: 1.0rem;
    border: solid 2px #ffffff;
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);/*影*/
}
/*ボタンの共通設定*/
.btn-wrapper {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;

}
.btn {
    font-weight: 700;
    padding: 1rem 4rem;
    margin: 1px;
    cursor: pointer;
    transition: all 0.3s;
    text-align: center;
    vertical-align: middle;
    border: solid 2px #ffffff;
    border-radius: 1.0rem;
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
}
/*スタートボタンの色*/
.btn-start {
    color: #fff;
    background-color: #6699ff;
    letter-spacing: 0.25rem;
    border-radius: 2.5rem;
}
.btn-start:hover {
    color: #fff;
    background: #70a1ff;
}
/*OKボタンの色*/
.btn-confirm {
    color: #fff;
    background-color: #99cc33;
}
.btn-confirm:hover {
    color: #fff;
    background: #a7d741;
}
.btn-white {
    font-weight: 700;
    padding: 1rem 2rem;
    margin: 1px;
    cursor: pointer;
    transition: all 0.3s;
    text-align: center;
    vertical-align: middle;
    border: solid 2px #ffffff;
    border-radius: 1.0rem;
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
    color: black;
    background-color: #f2f2f2;
}
/*大問表示*/
#question-number {
    font-weight: 700;
}
/*選択肢の枠線*/
.box-1 {
    padding: 0.5em 1em;
    margin: 0.25em 0;
    font-weight: bold;
    color: #6091d3;
    background: #FFF;
    border: solid 2px #6091d3;
    border-radius: 10px;
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
}
.box-1 p {
    margin: 0; 
    padding: 0;
}
/*バッジ*/
.badge {
    padding: 3px 6px;
    margin-right: 8px;
    margin-left: 1px;
    font-size: 75%;
    color: white;
    border-radius: 6px;
    box-shadow: 0 0 3px #ddd;
    white-space: nowrap;
    background-color: #58ACFA;
}
#home-btn {
    border: 1px solid #ccc;
    border-radius: 5px;
    padding: 10px;
    background: #f1e767;
    background: -webkit-gradient(linear, left top, left bottom, from(#fdfbfb), to(#ebedee));
    background: -webkit-linear-gradient(top, #fdfbfb 0%, #ebedee 100%);
    background: linear-gradient(to bottom, #fdfbfb 0%, #ebedee 100%);
    -webkit-box-shadow: inset 1px 1px 1px #fff;
    box-shadow: inset 1px 1px 1px #fff;
}
#home-btn:hover {
    background: -webkit-gradient(linear, left bottom, left top, from(#fdfbfb), to(#ebedee));
    background: -webkit-linear-gradient(bottom, #fdfbfb 0%, #ebedee 100%);
    background: linear-gradient(to top, #fdfbfb 0%, #ebedee 100%);
  }
#en-sentence {
    padding: 5px;
}