-4-1024x576.png)
タイピングゲームの2回目の機能追加。「間違えた文字を赤色で表示する」機能を実装しました。多数のエラーと格闘し、デバッグを重ねて完成させた記録です。
公開URL: https://dondokoboy.github.io/typing-game/
前回の機能追加(振り返り)
1回目:入力表示機能(午前)
実装内容:
- 入力した文字を青色で表示
- 未入力の文字は通常表示
結果: スムーズに実装完了
2回目の機能追加:間違い判定
目標
現状:
正解: apple
「apx」入力 → 青色(間違いでも青)
目指す姿:
正解: apple
「app」入力 → 青色(正解)
「apx」入力 → 赤色(不正解)
設計
やること:
- 入力文字が正解と一致しているか判定
- 正解なら青色、不正解なら赤色
実装方針:
startsWith()メソッドを使う- CSS に
.wrongクラスを追加 classNameで切り替え
パターンの選択
2つのパターン:
- パターンA: 間違えたら次に進めない
- パターンB: 間違えても続けられる
選択: パターンB
理由: 次の機能追加(間違い文字リストアップ)を見据えて
実装開始
Step 1: バックアップ作成
cp index.html index.html.backup
cp style.css style.css.backup
cp script.js script.js.backup
失敗に備えて。
Step 2: CSS追加
.wrongクラスを追加:
.wrong {
background-color: #e74c3c; /* 赤色 */
color: white;
padding: 2px 4px;
border-radius: 4px;
}
Step 3: JavaScript修正
updateWordDisplay関数を修正:
function updateWordDisplay(word, typedText) {
const enteredElement = document.querySelector('.entered');
const remainedElement = document.querySelector('.remained');
if (!enteredElement || !remainedElement) {
return;
}
const enteredPart = word.slice(0, typedText.length);
const remainedPart = word.slice(typedText.length);
enteredElement.textContent = enteredPart;
// 正解判定
if (word.startsWith(typedText)) {
enteredElement.className = 'entered'; // 青色
} else {
enteredElement.className = 'wrong'; // 赤色
}
remainedElement.textContent = remainedPart;
}
startsWith()メソッド:
"apple".startsWith("app")→true"apple".startsWith("apx")→false
エラーとの格闘
エラー1: Backspaceでエラー
症状: Backspaceを押すとエラー
エラー:
Uncaught TypeError: Cannot set properties of null (setting 'textContent')
原因: 要素が見つからない
解決: 安全策を追加
if (!enteredElement || !remainedElement) {
return;
}
エラー2: Backspace後に処理が止まる
症状:
- Backspaceでエラーは解消
- でも、正解を入力しても次の単語が出ない
デバッグ:
console.log('入力:', typedWord);
console.log('正解:', currentWord);
console.log('一致?:', typedWord === currentWord);
結果:
- 一致: true
- 「正解処理実行」と表示される
- でも次の単語が出ない
エラー3: showRandomWordでnull
エラー:
Uncaught TypeError: Cannot set properties of null (setting 'textContent')
showRandomWord @ script.js:33
原因: showRandomWord関数内で要素が見つからない
解決: 安全策を追加
const enteredElement = document.querySelector(".entered");
const remainedElement = document.querySelector(".remained");
if (enteredElement && remainedElement) {
enteredElement.textContent = "";
remainedElement.textContent = currentWord;
}
エラー4: enteredElement is not defined
エラー:
Uncaught ReferenceError: enteredElement is not defined
原因: 変数を定義せずに使っていた
間違い:
if (enteredElement && remainedElement) { // 未定義
修正:
const enteredElement = document.querySelector(".entered");
const remainedElement = document.querySelector(".remained");
if (enteredElement && remainedElement) {
エラー5: updateWordDisplay is not defined
エラー:
Uncaught ReferenceError: updateWordDisplay is not defined
原因:
showRandomWord関数が重複していたupdateWordDisplay関数が消えていた
解決: 関数を正しく配置し直す
エラー6: 最大の問題 – クラス名の消失
症状:
- 正しく入力 → 青色(OK)
- 間違えて入力 → 赤色(OK)
- Backspace → エラーなし(OK)
- 正しく入力 → 青色にならない
- 正解しても次の単語が出ない
デバッグ:
console.log('enteredElement:', enteredElement);
console.log('remainedElement:', remainedElement);
結果:
enteredElement: null
remainedElement: <span class="remained"></span>
.entered要素が見つからない!
原因の特定
updateWordDisplay関数で:
enteredElement.className = 'wrong'; // .enteredクラスを削除
→ .enteredクラスが消えて.wrongだけになる
→ 次にquerySelector('.entered')で探しても見つからない
解決策:複数のクラスを使う
HTML構造を変更:
変更前:
<span class="entered"></span>
<span class="remained">apple</span>
変更後:
<span class="typed-text entered"></span>
<span class="untyped-text remained">apple</span>
ポイント:
.typed-text: 固定のクラス(常に存在).enteredまたは.wrong: 状態に応じて切り替え
JavaScript修正:
querySelector:
document.querySelector('.typed-text') // これで確実に見つかる
document.querySelector('.untyped-text')
className設定:
// 正解
enteredElement.className = "typed-text entered";
// 不正解
enteredElement.className = "typed-text wrong";
これで.typed-textは残り続ける!
最終的なコード
updateWordDisplay関数
function updateWordDisplay(word, typedText) {
const enteredElement = document.querySelector(".typed-text");
const remainedElement = document.querySelector(".untyped-text");
if (!enteredElement || !remainedElement) {
return;
}
const enteredPart = word.slice(0, typedText.length);
const remainedPart = word.slice(typedText.length);
enteredElement.textContent = enteredPart;
// 正解判定
if (word.startsWith(typedText)) {
enteredElement.className = "typed-text entered"; // 正解:青色
} else {
enteredElement.className = "typed-text wrong"; // 不正解:赤色
}
remainedElement.textContent = remainedPart;
}
showRandomWord関数
function showRandomWord() {
const randomIndex = Math.floor(Math.random() * words.length);
currentWord = words[randomIndex];
const enteredElement = document.querySelector(".typed-text");
const remainedElement = document.querySelector(".untyped-text");
if (enteredElement && remainedElement) {
enteredElement.textContent = "";
remainedElement.textContent = currentWord;
// クラスをリセット
enteredElement.className = "typed-text entered";
}
}
完成した機能
動作
✅ 正しく入力 → 青色表示
✅ 間違えて入力 → 赤色表示
✅ Backspaceで修正 → 青色に戻る
✅ 正解したら次の単語へ
パターンB(間違えても続けられる)完成!
エラーとの格闘から学んだこと
技術面
複数のクラスを持つ要素:
<span class="class1 class2"></span>
- 複数のクラスをスペース区切りで指定
- 一部のクラスが変わっても、他は残る
querySelector の仕組み:
- クラスが1つでも一致すれば見つかる
- でも、完全一致で探すと見つからないことがある
className での上書き:
element.className = 'new-class'; // 全部置き換わる
- すべてのクラスが上書きされる
- 注意が必要
classList の使い方:
element.classList.add('class'); // 追加
element.classList.remove('class'); // 削除
- 個別に操作できる
- より安全
デバッグ技術
console.logの重要性:
- 何が起きているか可視化
- 予想と実際の違いを確認
- エラーの原因を特定
段階的なデバッグ:
- エラーメッセージを読む
- 行番号を確認
- console.logで値を確認
- 仮説を立てる
- 修正して確認
エラーは学びのチャンス:
- エラーが出ないと、問題に気づかない
- エラーを解決することで、深く理解できる
開発プロセス
バックアップの重要性:
- 失敗しても戻せる安心感
- 思い切って実装できる
段階的な実装:
- 一度に全部やらない
- 小さく確認しながら進める
あきらめない粘り強さ:
- 6つのエラーと格闘
- すべて解決できた
- 最後までやり遂げる力
1日の振り返り
午前:1回目の機能追加
入力表示機能(青色):
- スムーズに実装完了
- 自分で設計・実装
午後:2回目の機能追加
間違い判定機能(赤色):
- 多数のエラーと格闘
- デバッグを重ねて解決
- より深い理解を獲得
感想
「エラーは怖くない」
以前:
- エラーが出ると不安
- どうしていいか分からない
今:
- エラーメッセージを読む
- console.logで確認
- 段階的に解決
- エラーは学びのチャンス
「複雑な問題も解決できる」
今回のクラス名の問題:
- 原因の特定が難しい
- 解決方法が分かりにくい
でも:
- デバッグを重ねて原因を特定
- HTMLとJavaScript両方を修正
- 複雑な問題も解決できた
自信がついた。
「次はもっとスムーズに」
今回学んだこと:
- 複数のクラスの扱い方
- querySelector の仕組み
- デバッグ技術
次の機能追加では、もっとスムーズにできるはず。
次の機能追加(予定)
間違えた文字のリストアップ:
- 間違えた文字を記録
- リストとして表示
- リセット機能
今回パターンBを選んだ理由:
- 間違いを記録できる
- ゲームを続けながら蓄積できる
準備は整った。
Phase 2、着実に成長しています。
エラーと格闘しながら、深く理解する。
これが本物の学習です。
-2-1-120x68.png)
コメント