はじめに
この記事は初心者の個人的解釈多めな備忘録です。
よって事実と誤っている場合も多々ある可能性がございます。
ご容赦いただけますと幸いです。
前回作ったクイズゲームを拡張します
問題を増やす
問題の追加はquiz配列の中身を増やすだけでよかった。
長くなったQuiz配列を別ファイルに移す
何問も追加するとコードが見づらいような気もする。
右のレイアウト全体で分かるけどすごーく長い。
他コードと区別しておいたほうが、見やすいコードになりそうです。

VScodeにGithub Copilotを入れたので、やり方はどんなもんか聞いてみた!
実装例(あなたのプロジェクト向け)
quiz 配列を別ファイルに移す(quiz-data.js)
app.js の先頭で import する
index.html で app.js を type="module" で読み込む
moduleという仕組みで、
別ファイルでexportしておいた内容をimportで受け取ることができるようです。
・quiz配列を別ファイルに移す
//定数で問題文、選択肢、答えを3問分宣言
const quiz = [
{
question: 'ドラゴンボール原作で二番目に強いとされるキャラクターは誰?',
answers: [
'魔人ブウ(孫悟飯吸収)',
'魔人ブウ(ゴテンクス吸収)',
'孫悟空(超サイヤ人3)',
'ベジータ(魔人化)'],
correct: '魔人ブウ(孫悟飯吸収)' //答え
}
,
//省略
{
question: '物語の途中で尻尾が再生したことがあるキャラは誰?',
answers: [
'孫悟空',
'ベジータ',
'孫悟飯',
'ラディッツ',],
correct: '孫悟空'
}
]
export default quiz; //外部で読み込みたいものをexportしてあげる。
・app.jsの先頭でimportする
//問題文、選択肢、正解オブジェクトの配列をインポート
import quiz from './quiz_data.js';
//後々while文で使う
const quizLength = quiz.length;
let quizIndex = 0;
//クイズの正解数
let score = 0;
・htmlでtype=moduleとしてjsを読み込み
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Quiz</title>
</head>
<body>
<!-- ...既存の HTML ... -->
<script type="module" src="app.js"></script>
</body>
</html>
これでOKか?と思いきや・・・
jsファイルが読み込めてないっぽいです。

ローカルサーバーをたてる
ブラウザは file://(ローカルファイル)からのモジュール読み込みをセキュリティ(CORS)で制限するため、ES module を import する場合は HTTP サーバで配信する必要があります。
やること(簡潔)
・index.html の script に type="module" を使っていることを確認する。
・プロジェクトをローカル HTTP サーバで配信し、http://localhost:PORT/ で開く。
ブラウザのセキュリティ上、ローカルファイルのモジュール読み込みができず、
ローカルサーバーにサイトを置く必要があるようです。
ローカルサーバーの立て方。
cmdからpythonで立てる方法もあるそうですが
# プロジェクトへ移動
cd "D:\Programing\Quiz"
# Python の場合
py -m http.server 8000
# または
python -m http.server 8000
# Node がある場合
npx http-server . -p 8000
せっかく拡張機能があるのでそっちを使ってみました。

ありがとうAIくん

・プロジェクトのルートフォルダを"フォルダーとして開く"
・表示-外観からステータスバーを表示
・vscode右下(ステータスバー上)のGo Liveを押すと

ローカルサーバーが立ち、サイトが見れました。
ここでならjsファイルをmodule読み込みできてるっぽい
いずれインターネットで見れるようにするまではこれで確認します

トップ画面、結果画面を作る。
一から作るのが難しそうなのでAIで一度作ってもらい、そこからコードを学習してみる。
理解するにはAIのコードを横に並べて、自分も別ファイルにコードを書き写そうかなと思います。
AIに聞いたらソースURLやドキュメント、リファレンスも出すようにしてもらう
ドキュメントも読めるようになったほうがいい、ドキュメントのカタカナが分からないならそれをAIに説明させるなど。
https://developer.mozilla.org/ja/docs/Web/HTML/Reference
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference
https://getbootstrap.jp/docs/5.3/getting-started/introduction/
・まず全体の構造を見てどんな宣言や命令文で作っているのかをおおまかに確認。
・一行ずつ意味を理解してすべて書き写す。
タイトル画面
AIへプロンプトを投げる。
スタート画面htmlとスタート画面用のjsファイルを作りたいです。
スタート画面の構造は画面真ん中上に「ドラゴンボールクイズ」というタイトル
その下に「スタート」というボタンがあり、それを押すとindex.htmlに遷移します。
ちなみに気になったので
webサイトで表示するとき一番初めに使うhtmlってどうやって指定するの?
ブラウザがルート(/)で最初に読み込むファイルは通常 index.html です。
start.html を最初に表示したい → ファイル名を index.html にリネームする(最も簡単)。
サーバー設定で start.html を既定ページにする → サーバー側で指定する(下記例参照
まじか。ということでスタート画面をindex.html、クイズ画面はapp.htmlに変更します。
<!doctype html> <!-- HTML5 宣言 -->
<html lang="ja"> <!-- ページ言語を日本語に設定 -->
<head>
<meta charset="utf-8"> <!-- 文字エンコーディングを UTF-8 に指定 -->
<meta name="viewport" content="width=device-width, initial-scale=1"> <!-- レスポンシブ対応のビューポート設定 -->
<title>ドラゴンボールクイズ - スタート</title> <!-- ブラウザタブに表示されるタイトル -->
<!-- Bootstrap の CSS を CDN から読み込む(ページの見た目を簡単に整える) -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
crossorigin="anonymous">
</head>
<body>
<!-- 全画面を使う Flex コンテナ(縦方向) -->
<div class="d-flex flex-column align-items-center" style="min-height:100vh; padding-top:12vh;">
<!-- d-flex: Flexbox を有効化、flex-column: 子要素を縦方向に並べる、align-items-center: 横方向中央揃え -->
<!-- タイトル(画面上寄り中央に表示される) -->
<h1 class="mb-4">ドラゴンボールクイズ</h1>
<!-- h1: 見出し、mb-4: 下マージンを付けてボタンと間隔を空ける -->
<!-- スタートボタン(押すと index.html に遷移) -->
<button id="start-btn" class="btn btn-primary btn-lg">
スタート
</button>
<!-- id="start-btn": index.js で選択するための識別子、btn/btn-primary/btn-lg: Bootstrap のボタンスタイル -->
</div>
<!-- スタート画面用のスクリプトを読み込む -->
<script type="module" src="./index.js"></script>
<!-- type="module": ES モジュールとして読み込む(import/export が使える場合に推奨) -->
</body>
</html>
head内
「meta name="viewport" content="width=device-width, initial-scale=1"」
ビューポートとは?
ブラウザが「ページを描画するための仮想的な表示領域(幅と高さ)」と考える領域のこと。PCではウィンドウ幅とほぼ同じだが、スマホでは機種ごとの論理ピクセル幅(device-width)で扱われる。
モバイル表示の際、自動で縮小されるとまずい点
レイアウトが意図どおりに見えない(デスクトップ向け幅をそのまま縮小表示するため)。
CSS のメディアクエリが期待通り発動しない → レスポンシブデザインが効かない。
文字やボタンが極端に小さくなり操作しづらい(アクセシビリティ低下)。
タッチターゲットが小さく誤操作が増える。
対策(一般的)
head 内に次のメタタグを入れる(ほとんどのサイトでこれでOK)
「style="min-height:100vh; padding-top:12vh」
padding-topは画面上部の余白12%
min-height:100vhは拡大縮小したときに表示される高さ?100%で表示を指定?
何やら問題点もあるようですが、実際に直面したときに直そうと思います。
今後のために参考サイト様を記しておきます
https://web-camp.io/magazine/archives/91511/
https://www.tak-dcxi.com/article/that-css-technique-you-learned-is-outdated/
body内
まず見出しの”ドラゴンボールクイズ”と画面遷移用のスタートボタンを作成
そのあとにコンテナで整列させる。
勉強のため確認したら、
bootstrapの補助なしでcssを書くとこうなるっぽいです。
フォントやボタンの設計図(クラス)をあらかじめ書いておき、
それをbody内の「class=名前」で呼び出しているんですね。
<!doctype html> <!-- HTML5 宣言 -->
<html lang="ja">
<head>
<meta charset="utf-8"> <!-- 文字コード -->
<meta name="viewport" content="width=device-width, initial-scale=1"> <!-- レスポンシブ対応 -->
<title>ドラゴンボールクイズ - スタート</title>
<!-- 自前のスタイル(Bootstrap を使わない場合はここに書く) -->
<style>
/* ページ全体のリセットと基本フォント */
* { box-sizing: border-box; }
body {
margin: 0;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "メイリオ", sans-serif;
background: #f8f9fa;
color: #111;
}
/* 画面いっぱいを使うレイアウト(flex で縦配置) */
.full-screen {
min-height: 100vh; /* ビューポート高さの 100% を最低高さにする */
padding-top: 12vh; /* 上部余白を画面高さの12%に */
display: flex;
flex-direction: column;
align-items: center; /* 横中央揃え */
justify-content: flex-start;
}
/* タイトルの見た目 */
.title {
margin: 0 0 2rem 0;
font-size: 2rem;
text-align: center;
}
/* ボタンのスタイル(Bootstrap っぽい見た目を自作) */
.btn {
appearance: none;
border: none;
background: #0d6efd;
color: #fff;
padding: 0.9rem 1.6rem;
font-size: 1.125rem;
border-radius: 0.5rem;
cursor: pointer;
box-shadow: 0 2px 6px rgba(13,110,253,0.2);
}
.btn:active { transform: translateY(1px); }
.btn:focus { outline: 3px solid rgba(13,110,253,0.2); }
/* 小さい画面向け調整 */
@media (max-width: 480px) {
.title { font-size: 1.5rem; }
.btn { padding: 0.7rem 1.2rem; font-size: 1rem; }
}
</style>
</head>
<body>
<!-- スタート画面 -->
<div class="full-screen">
<h1 class="title">ドラゴンボールクイズ</h1> <!-- タイトル -->
<button id="start-btn" class="btn" type="button">スタート</button> <!-- スタートボタン -->
</div>
<!-- スタート画面用のスクリプトを読み込む -->
<script type="module" src="./index.js"></script>
<!-- type="module": ES モジュールとして読み込む(import/export が使える場合に推奨) -->
</body>
</html>
// start.js: スタート画面のボタン操作(index.html へ遷移)を担当するファイル
// スタートボタン要素を取得する(id='start-btn')
const startBtn = document.getElementById('start-btn'); // DOM からボタンを取得
// 取得できているか簡単にチェック(デバッグ用、必要なければ削除可)
if (!startBtn) {
// ボタンが見つからない場合はコンソールに警告を出す
console.warn('start-btn が見つかりません。index.html を正しい場所に保存してください。');
} else {
// ボタンが存在する場合、クリック時の処理を登録する
startBtn.addEventListener('click', () => {
// クリック時に app.html に遷移する(同一フォルダ内の app.html を開く)
window.location.href = './app.html'; // './app.html' は相対パス。必要なら絶対パスやURLに変更可
});
}
const startBtn = document.getElementById('start-btn');
スタートボタンをhtmlから取得
!startBtn
!はJavaScriptの論理否定(logical NOT)演算子
右側の値を「真/偽(truthy/falsy)」で評価
値が「true(真扱い)」なら !値 は false
値が「false(偽扱い)」なら !値 は true
Location.href
URL全体を収めた文字列を取得。
この値に代入することで、別のURLへ移動できる。
<!doctype html> <!-- HTML5 宣言 -->
<html lang="ja"> <!-- ページ言語を日本語に設定 -->
<head>
<meta charset="utf-8"> <!-- 文字エンコーディングを UTF-8 に指定 -->
<meta name="viewport" content="width=device-width, initial-scale=1"> <!-- レスポンシブ対応のビューポート設定 -->
<title>ドラゴンボールクイズ - スタート</title> <!-- ブラウザタブに表示されるタイトル -->
<!-- Bootstrap の CSS を CDN から読み込む(ページの見た目を簡単に整える) -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
crossorigin="anonymous">
</head>
<body>
<!-- 全画面を使う Flex コンテナ(縦方向) -->
<div class="d-flex flex-column align-items-center" style="min-height:2vh; padding-top:12vh;">
<!-- d-flex: Flexbox を有効化、flex-column: 子要素を縦方向に並べる、align-items-center: 横方向中央揃え -->
<!-- タイトル(画面上寄り中央に表示される) -->
<h1 class="mb-4" style="font-size: 50px;">ドラゴンボールクイズ</h1>
<!-- h1: 見出し、mb-4: 下マージンを付けてボタンと間隔を空ける -->
<!-- スタートボタン(押すと index.html に遷移) -->
<button id="start-btn" class="btn btn-primary btn-lg">
スタート
</button>
<!-- id="start-btn": index.js で選択するための識別子、btn/btn-primary/btn-lg: Bootstrap のボタンスタイル -->
</div>
<!-- スタート画面用のスクリプトを読み込む -->
<script type="module" src="./index.js"></script>
<!-- type="module": ES モジュールとして読み込む(import/export が使える場合に推奨) -->
</body>
</html>
// start.js: スタート画面のボタン操作(index.html へ遷移)を担当するファイル
// スタートボタン要素を取得する(id='start-btn')
const $startBtn = document.getElementById('start-btn'); //ボタンを取得
//クリック時の処理を登録する
$startBtn.addEventListener('click', () => {
// クリック時に app.html に遷移する(同一フォルダ内の app.html を開く)
window.location.href = './app.html'; // './app.html' は相対パス。必要なら絶対パスやURLに変更可
});
リザルト画面をつくる
AIへ投げたプロンプト
app.htmlの問題を15問解き終わった後に遷移する結果画面を作りたいです。
result.htmlとresult.jsを作ります。
あなたの正答数は の後にスコアを出します。
既存ファイルの変更がある場合は、変更行のコメントアウトに★を付けてください。
Webサイトとして扱いたいです。ローカルストレージというのは、AWSなどのサーバー上でも機能しますか?
AIからの回答
ローカルストレージについて(簡潔)
localStorage はブラウザ側(クライアント)の「同一オリジン(スキーム+ホスト+ポート)」ごとのストレージです。
AWS などのサーバーにホストしても、ブラウザに保存される点は変わりません(ホスティング先は関係ありません)。
そのため、localStorage はユーザーごと・端末ごとに分かれており、複数ユーザーで共有されません。
サーバーで全ユーザーのスコアを保存・集計したい場合は、API(POST)でサーバーへ送信して DB に保存する実装が必要です(例: fetch で送ってサーバー側で保存)。
必要なら、"サーバー保存用の簡単な API 例(fetch + Node/Express の受け取り)" を提示しますか?
// ...existing code...
//ボタンをクリックしたら正誤判定する
//e.targetの内容を正誤判定する関数
const clickHandler = (e) => {
//文字列の一致を判定するときは === を使う
if(quiz[quizIndex].correct === e.target.textContent){
//正解したらスコアを足してあげる。
score++;
//windowオブジェクトのアラート関数を呼び出し
window.alert('正解! 現在の正解数' + score + '/' + quizLength);
}
else{
window.alert('不正解! 現在の正解数' + score + '/' + quizLength);
}
quizIndex++;
if(quizIndex < quizLength){
//問題数がまだあればこちらを実行
setupQuiz();
}
else{
//問題数がもうなければこちらを実行
try {
localStorage.setItem('quizScore', String(score)); // ★ ローカルストレージに得点を保存
localStorage.setItem('quizLength', String(quizLength)); // ★ ローカルストレージに問題数を保存
} catch (err) {
console.error('localStorage error:', err); // ★ 保存失敗時はコンソールに出す
}
window.location.href = './result.html';
}
};
// ...existing code...
まずはスコアを保存するスクリプトを書く。
ここで保存したスコアをリザルト画面で呼び出す。
localStorage.setItem('識別子','保存したい文字列');
try catchはtry内のコードを実行し、例外が出た際にはcatch内のコードを実行する。
catch(err)のerrはtry文で例外が出た際のエラーログを受け取るための変数名、別の名前でも機能する。
console.error()はコンソールにエラー文としてログを表示する
ここではerrの内容を表示することにしている
<!doctype html> <!-- ★ HTML5 宣言: ドキュメントが HTML5 であることを示す -->
<html lang="ja"> <!-- ★ ページの言語を日本語に設定(アクセシビリティとSEO向け) -->
<head>
<meta charset="utf-8"> <!-- ★ 文字エンコーディングを UTF-8 に指定 -->
<meta name="viewport" content="width=device-width, initial-scale=1"> <!-- ★ レスポンシブ対応: ビューポート設定 -->
<title>クイズ結果 - ドラゴンボールクイズ</title> <!-- ★ ブラウザタブに表示されるタイトル -->
<!-- Bootstrap CSS を CDN から読み込む(見た目を簡単に整えるため) -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
crossorigin="anonymous"> <!-- ★ CDN 読み込み: 安全性のため integrity と crossorigin を指定 -->
</head>
<body>
<!-- ★ container: コンテンツを中央に寄せる Bootstrap のラッパー -->
<!-- ★ vh-100: 表示領域の高さを 100% にして縦中央に配置 -->
<div class="container vh-100 d-flex flex-column justify-content-center align-items-center">
<h1 class="mb-3">結果</h1> <!-- ★ ページ見出し: 結果ページであることを示す -->
<!-- ★ result-box: JavaScript(result.js) がここを書き換えてスコア表示を行う -->
<div id="result-box" class="alert alert-info text-center" role="alert">
<!-- ★ 初期テキスト(読み込み中): JS が結果を読み込むまで表示される -->
あなたの正答数は 0 / 0 です。読み込み中...
</div>
<!-- ★ ボタン群: 再挑戦またはスタート画面へ戻る -->
<div class="d-flex gap-2 mt-3">
<button id="restart-btn" class="btn btn-primary">もう一度挑戦</button> <!-- ★ restart-btn: スコアを消して app.html に戻る -->
<button id="to-startpage-btn" class="btn btn-secondary">スタート画面へ</button> <!-- ★ to-startpage-btn: index.html に戻る -->
</div>
</div>
<!-- ★ result.js を読み込む: localStorage からスコアを取得して表示・ボタン処理を行う -->
<script type="module" src="./result.js"></script>
<!-- ★ type="module": ESモジュールとして読み込む(import/export を使う場合に推奨) -->
</body>
</html>
body内
見出し”結果”、結果内容、再挑戦ボタン、スタート画面に戻るボタンを記述。
Containerでコンテンツを中央に配置し、左右に余白を設ける
flex columnで要素を縦に整列させる
ボタンをさらにflexで囲って横ならべにする。
// result.js: localStorage からスコアを読み取り結果を表示する
const resultBox = document.getElementById('result-box'); // 結果表示要素
const restartBtn = document.getElementById('restart-btn'); // 再挑戦ボタン
const toStartpageBtn = document.getElementById('to-startpage-btn'); // スタート画面へ戻るボタン
// localStorage からデータを取り出す
const rawScore = localStorage.getItem('quizScore');
const rawLength = localStorage.getItem('quizLength');
if (rawScore === null || rawLength === null) {
// データが無ければメッセージ表示
resultBox.textContent = '結果データが見つかりませんでした。最初からプレイしてください。';
} else {
const score = Number(rawScore);
const length = Number(rawLength);
resultBox.textContent = `あなたの正答数は ${score} / ${length} です。`;
}
// 再挑戦:app.html を開く(前回のスコアをクリア)
restartBtn.addEventListener('click', () => {
try {
localStorage.removeItem('quizScore');
localStorage.removeItem('quizLength');
} catch (err) {
console.warn('localStorage clear failed', err);
}
window.location.href = './app.html';
});
// スタート画面へ戻る
toStartpageBtn.addEventListener('click', () => {
window.location.href = './index.html';
});
ここでは結果内容のテキストの変更、再挑戦ボタンからのapp.htmlへの遷移、スタート画面へ戻るボタンからのindex.htmlへの遷移をする。
const resultBox = document.getElementById('result-box');
まずはオブジェクトを取得。
const rawScore = localStorage.getItem('quizScore');
app.jsで保存したスコアを取得
if (rawScore === null || rawLength === null)
でスコアが存在する場合はresultboxのテキストを書き換える。
addeventlisnerで再挑戦、スタート画面へボタンをクリックした際、
スコアを削除してから画面へ遷移させる。
ローカルでランキング機能を作る(編集中)
そういえば画面設計図を書いてないことを思い出した
ランキング画面については書いてみよう..!


ランキングをリザルト画面の下へ表示したい
月リセットのランキング
名前とスコア 10位くらい
ランキング機能をAWSのデータベースを使い、ネット上で共有可能にする(編集中)
AWSのデータベースに名前とスコアを保存して、
全ユーザー間でスコアランキングを共有したい。
スコアをランキングにしたい。 AWSのデータベースも使う
S3にはどのタイミングで実装する?
短く結論:
バックエンド(API/Lambda + DynamoDB/RDS 等)を先に完成させて公開(CORS 設定含む)→ フロントを本番用 S3 にデプロイするのが安全で簡単です。
テスト用に「先に S3(またはローカルホスト)に置いてフロントだけ動かす」ことも可能だが、API が未完成だとランキング周りは動作しない/エラーになる。
推奨手順(端的)
API(POST /score, GET /top)と DB を作る・公開。CORS を本番オリジンに許可。
ローカルでフロントを API に接続して動作確認(fetch のエンドポイントを一時的に設定)。
ステージング用 S3+CloudFront にデプロイして統合テスト(HTTPS・キャッシュ確認)。
問題なければ本番 S3+CloudFront に反映、DNS/SSL 設定、CI/CD を整備。
注意点(短く)
API_BASE(エンドポイント)は環境ごとに切替可能にする(ビルド時変数 or config ファイル)。
スパム防止・検証はサーバ側で必須。
カスタムドメイン・HTTPS を使うなら CloudFront を入れる。
app.js に API 呼び出しを入れる例(保存直後に送信):
問題をランダムに出題(編集中)
例えば百問くらいの中からランダムで15問だけ出したい。
問題数が多くなる場合はjsonファイルにしたほうがいいかも。
問題回答時の正誤アラートの変更(編集中)
毎回アラートのOKを押すのが煩わしいので、とりあえず削除
今後正誤が分かりつつ、OKを押さなくていい通知方法を実装したい



