JavaScript | エスケープシーケンス(特殊文字の記述)

javascrpit JavaScript
スポンサーリンク

実際に「バグ → 修正」を体験できる コード演習Webアプリ(自動採点付き)

アプリ概要

  • エスケープ関連のバグを含んだサンプルコードが表示される
  • ユーザーが修正して「実行」ボタンを押す
  • 結果が正しければ「正解!」、違えば「ヒント」が表示される
  • ローカルストレージに正答率を保存(復習にも使える)

コード全体(ブラウザで即動作)

下のコードを HTMLファイルとして保存し、ブラウザで開くだけ で動きます。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>エスケープシーケンス修正演習</title>
<style>
  body { font-family: "Segoe UI", sans-serif; padding: 2rem; background: #fafafa; }
  h1 { color: #333; }
  pre { background: #f5f5f5; padding: 1em; border-radius: 8px; }
  textarea { width: 100%; height: 120px; font-family: monospace; }
  button { margin-top: 10px; padding: 8px 16px; font-size: 16px; cursor: pointer; border-radius: 6px; }
  #result { margin-top: 1em; font-weight: bold; }
  .correct { color: green; }
  .wrong { color: crimson; }
  .hint { color: #555; margin-top: 0.5em; font-style: italic; }
  .progress { margin-top: 1em; font-size: 14px; }
</style>
</head>
<body>

<h1>🧩 エスケープシーケンス修正演習</h1>
<p>次のコードを正しく直して「実行」を押してみましょう。</p>

<pre id="question"></pre>
<textarea id="answer"></textarea><br />
<button id="runBtn">実行</button>
<div id="result"></div>
<div class="progress" id="progress"></div>

<script>
const exercises = [
  {
    question: "console.log('It's a pen.');",
    expected: "It's a pen.",
    hint: "シングルクォート内の ' をエスケープしよう。"
  },
  {
    question: "console.log('C:\\Users\\Halu\\Desktop'); // ←修正前に \\ を一つ消してみよう",
    expected: "C:\\Users\\Halu\\Desktop",
    hint: "バックスラッシュは2つ重ねよう。"
  },
  {
    question: "const msg = 'Hello,\nWorld!'; // ←改行して直接書くとエラーになる",
    expected: "Hello,\nWorld!",
    hint: "改行は \\n またはバッククォートで。"
  },
  {
    question: "console.log('\\u1F600'); // ←絵文字にしたい",
    expected: "😀",
    hint: "5桁以上のUnicodeは \\u{1F600} の形で。"
  }
];

let current = 0;
let correctCount = 0;

function loadQuestion() {
  const q = exercises[current];
  document.getElementById("question").textContent = q.question;
  document.getElementById("answer").value = q.question;
  document.getElementById("result").textContent = "";
}

function checkAnswer() {
  const userCode = document.getElementById("answer").value;
  try {
    const output = eval(userCode);
    if (output === exercises[current].expected) {
      document.getElementById("result").innerHTML = "✅ 正解! よくできました 🎉";
      document.getElementById("result").className = "correct";
      correctCount++;
      nextQuestion();
    } else {
      document.getElementById("result").innerHTML = "❌ 出力が違います。<div class='hint'>ヒント:" + exercises[current].hint + "</div>";
      document.getElementById("result").className = "wrong";
    }
  } catch (e) {
    document.getElementById("result").innerHTML = "⚠️ エラー:" + e.message + "<div class='hint'>ヒント:" + exercises[current].hint + "</div>";
    document.getElementById("result").className = "wrong";
  }
}

function nextQuestion() {
  current++;
  if (current < exercises.length) {
    setTimeout(loadQuestion, 1500);
  } else {
    localStorage.setItem("escapeQuizScore", correctCount);
    document.getElementById("question").textContent = "🎉 すべての問題をクリアしました!";
    document.getElementById("answer").style.display = "none";
    document.getElementById("runBtn").style.display = "none";
    document.getElementById("result").textContent = `あなたの正答数:${correctCount}/${exercises.length}`;
  }
  document.getElementById("progress").textContent = `進捗:${correctCount}/${exercises.length}`;
}

document.getElementById("runBtn").addEventListener("click", checkAnswer);
loadQuestion();
</script>

</body>
</html>
HTML

このアプリでできること

  • 実際に「誤ったコード」を直して動作を確認できる
  • 実行結果を自動判定
  • 進捗を記録(localStorage使用)
  • ヒントで学びながら理解を深める

難易度選択モード(初級/中級/上級)

アプリ概要

  • 起動時に「初級/中級/上級」から選択
  • 難易度ごとに問題内容とヒントが変化
  • 正答率をレベル別に記録
  • 各レベルをクリアすると「レベルアップ」演出付き

完成版コード

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>エスケープシーケンス修正演習【難易度モード付き】</title>
<style>
  body { font-family: "Segoe UI", sans-serif; padding: 2rem; background: #fdfdfd; }
  h1 { color: #333; }
  pre { background: #f5f5f5; padding: 1em; border-radius: 8px; }
  textarea { width: 100%; height: 120px; font-family: monospace; }
  button { margin: 8px 6px; padding: 8px 16px; font-size: 16px; cursor: pointer; border-radius: 6px; border: none; }
  #runBtn { background: #4caf50; color: white; }
  #result { margin-top: 1em; font-weight: bold; }
  .correct { color: green; }
  .wrong { color: crimson; }
  .hint { color: #555; margin-top: 0.5em; font-style: italic; }
  .progress { margin-top: 1em; font-size: 14px; }
  #levelSelect button { background: #2196f3; color: white; }
  #levelSelect { margin-bottom: 1.5em; }
</style>
</head>
<body>

<h1>🧩 エスケープシーケンス修正演習</h1>
<div id="levelSelect">
  <p>🎯 難易度を選んでください:</p>
  <button onclick="startLevel('初級')">初級</button>
  <button onclick="startLevel('中級')">中級</button>
  <button onclick="startLevel('上級')">上級</button>
</div>

<pre id="question"></pre>
<textarea id="answer" style="display:none;"></textarea><br />
<button id="runBtn" style="display:none;">実行</button>
<div id="result"></div>
<div class="progress" id="progress"></div>

<script>
const levels = {
  "初級": [
    {
      question: "console.log('It’s a pen.'); // ← エラーを修正",
      expected: "It's a pen.",
      hint: "シングルクォート内の ' をエスケープしよう(\\')。"
    },
    {
      question: "console.log('Hello\\nWorld'); // ← 改行したい",
      expected: "Hello\nWorld",
      hint: "改行には \\n を使おう。"
    }
  ],
  "中級": [
    {
      question: "console.log('C:\\Users\\Halu\\Desktop'); // ← バックスラッシュが1つだとどうなる?",
      expected: "C:\\Users\\Halu\\Desktop",
      hint: "バックスラッシュは2つ重ねて書く必要がある。"
    },
    {
      question: "console.log('She said, \"Hello!\"'); // ← 引用符をエスケープしてみよう",
      expected: 'She said, "Hello!"',
      hint: "ダブルクォート内では \\\" を使う。"
    }
  ],
  "上級": [
    {
      question: "console.log('\\u1F600'); // ← 絵文字にしたい",
      expected: "😀",
      hint: "5桁以上のUnicodeは \\u{1F600} のように書く。"
    },
    {
      question: "console.log(`改行を直接書く
エラーになる`); // ←修正しよう",
      expected: "改行を直接書くと\nエラーになる",
      hint: "文字列内で改行したい場合は \\n を使う。"
    }
  ]
};

let currentLevel = "";
let exercises = [];
let current = 0;
let correctCount = 0;

function startLevel(level) {
  currentLevel = level;
  exercises = levels[level];
  current = 0;
  correctCount = 0;
  document.getElementById("levelSelect").style.display = "none";
  document.getElementById("answer").style.display = "";
  document.getElementById("runBtn").style.display = "";
  loadQuestion();
}

function loadQuestion() {
  const q = exercises[current];
  document.getElementById("question").textContent = q.question;
  document.getElementById("answer").value = q.question;
  document.getElementById("result").textContent = "";
  updateProgress();
}

function checkAnswer() {
  const userCode = document.getElementById("answer").value;
  try {
    const output = eval(userCode);
    if (output === exercises[current].expected) {
      document.getElementById("result").innerHTML = "✅ 正解! よくできました 🎉";
      document.getElementById("result").className = "correct";
      correctCount++;
      nextQuestion();
    } else {
      document.getElementById("result").innerHTML = "❌ 出力が違います。<div class='hint'>ヒント:" + exercises[current].hint + "</div>";
      document.getElementById("result").className = "wrong";
    }
  } catch (e) {
    document.getElementById("result").innerHTML = "⚠️ エラー:" + e.message + "<div class='hint'>ヒント:" + exercises[current].hint + "</div>";
    document.getElementById("result").className = "wrong";
  }
}

function nextQuestion() {
  current++;
  if (current < exercises.length) {
    setTimeout(loadQuestion, 1500);
  } else {
    saveScore();
    document.getElementById("question").textContent = `🎉 ${currentLevel}コースクリア!`;
    document.getElementById("answer").style.display = "none";
    document.getElementById("runBtn").style.display = "none";
    document.getElementById("result").textContent = `正答数:${correctCount}/${exercises.length}`;
    document.getElementById("result").className = "correct";
  }
  updateProgress();
}

function updateProgress() {
  document.getElementById("progress").textContent =
    `進捗:${correctCount}/${exercises.length}(${currentLevel})`;
}

function saveScore() {
  const key = "escapeQuiz_" + currentLevel;
  localStorage.setItem(key, correctCount + "/" + exercises.length);
}

document.getElementById("runBtn").addEventListener("click", checkAnswer);
</script>

</body>
</html>
HTML

特徴まとめ

機能説明
🎯 難易度選択初級・中級・上級から選べる
🧩 出題形式バグを修正するエスケープ演習
⚙️ 自動採点eval()で実行 → 出力一致判定
💡 ヒント表示出力が違うとヒントを表示
💾 スコア保存localStorageで履歴管理
🏅 レベルごとにスコア記録各モードの成績を独立保存

グラフで正答率を可視化

アプリ概要

  • 各プレイ終了時に 正答率を localStorage に記録
  • 過去の履歴をもとに 棒グラフで推移を可視化
  • Chart.js(軽量&人気のライブラリ)を使用

完成版コード

下のコードをそのまま escape_quiz_graph.html として保存し、ブラウザで開くだけです。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>エスケープシーケンス修正演習 + グラフ可視化</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
  body { font-family: "Segoe UI", sans-serif; padding: 2rem; background: #fafafa; }
  h1 { color: #333; }
  pre { background: #f5f5f5; padding: 1em; border-radius: 8px; }
  textarea { width: 100%; height: 120px; font-family: monospace; }
  button { margin-top: 10px; padding: 8px 16px; font-size: 16px; cursor: pointer; border-radius: 6px; }
  #result { margin-top: 1em; font-weight: bold; }
  .correct { color: green; }
  .wrong { color: crimson; }
  .hint { color: #555; margin-top: 0.5em; font-style: italic; }
  .progress { margin-top: 1em; font-size: 14px; }
  #chartContainer { margin-top: 2em; display: none; }
</style>
</head>
<body>

<h1>🧩 エスケープシーケンス修正演習</h1>
<p>次のコードを正しく直して「実行」を押してみましょう。</p>

<pre id="question"></pre>
<textarea id="answer"></textarea><br />
<button id="runBtn">実行</button>
<div id="result"></div>
<div class="progress" id="progress"></div>

<div id="chartContainer">
  <h2>📊 あなたの学習履歴(正答率)</h2>
  <canvas id="scoreChart" width="400" height="200"></canvas>
</div>

<script>
const exercises = [
  {
    question: "console.log('It's a pen.');",
    expected: "It's a pen.",
    hint: "シングルクォート内の ' をエスケープしよう。"
  },
  {
    question: "console.log('C:\\Users\\Halu\\Desktop'); // ←修正前に \\ を一つ消してみよう",
    expected: "C:\\Users\\Halu\\Desktop",
    hint: "バックスラッシュは2つ重ねよう。"
  },
  {
    question: "const msg = 'Hello,\nWorld!'; // ←改行して直接書くとエラーになる",
    expected: "Hello,\nWorld!",
    hint: "改行は \\n またはバッククォートで。"
  },
  {
    question: "console.log('\\u1F600'); // ←絵文字にしたい",
    expected: "😀",
    hint: "5桁以上のUnicodeは \\u{1F600} の形で。"
  }
];

let current = 0;
let correctCount = 0;

function loadQuestion() {
  const q = exercises[current];
  document.getElementById("question").textContent = q.question;
  document.getElementById("answer").value = q.question;
  document.getElementById("result").textContent = "";
}

function checkAnswer() {
  const userCode = document.getElementById("answer").value;
  try {
    const output = eval(userCode);
    if (output === exercises[current].expected) {
      document.getElementById("result").innerHTML = "✅ 正解! よくできました 🎉";
      document.getElementById("result").className = "correct";
      correctCount++;
      nextQuestion();
    } else {
      document.getElementById("result").innerHTML = "❌ 出力が違います。<div class='hint'>ヒント:" + exercises[current].hint + "</div>";
      document.getElementById("result").className = "wrong";
    }
  } catch (e) {
    document.getElementById("result").innerHTML = "⚠️ エラー:" + e.message + "<div class='hint'>ヒント:" + exercises[current].hint + "</div>";
    document.getElementById("result").className = "wrong";
  }
}

function nextQuestion() {
  current++;
  if (current < exercises.length) {
    setTimeout(loadQuestion, 1500);
  } else {
    const rate = Math.round((correctCount / exercises.length) * 100);
    saveScore(rate);
    showResult(rate);
  }
  document.getElementById("progress").textContent = `進捗:${correctCount}/${exercises.length}`;
}

function saveScore(rate) {
  const history = JSON.parse(localStorage.getItem("escapeQuizHistory") || "[]");
  history.push({ date: new Date().toLocaleString(), rate });
  localStorage.setItem("escapeQuizHistory", JSON.stringify(history));
}

function showResult(rate) {
  document.getElementById("question").textContent = "🎉 すべての問題をクリアしました!";
  document.getElementById("answer").style.display = "none";
  document.getElementById("runBtn").style.display = "none";
  document.getElementById("result").textContent = `あなたの正答率:${rate}%`;
  document.getElementById("chartContainer").style.display = "block";
  renderChart();
}

function renderChart() {
  const ctx = document.getElementById("scoreChart").getContext("2d");
  const history = JSON.parse(localStorage.getItem("escapeQuizHistory") || "[]");
  const labels = history.map((h, i) => `#${i+1} (${h.date.split(' ')[0]})`);
  const data = history.map(h => h.rate);
  new Chart(ctx, {
    type: 'bar',
    data: {
      labels,
      datasets: [{
        label: '正答率(%)',
        data,
        borderWidth: 1
      }]
    },
    options: {
      scales: { y: { beginAtZero: true, max: 100 } }
    }
  });
}

document.getElementById("runBtn").addEventListener("click", checkAnswer);
loadQuestion();
</script>

</body>
</html>
HTML

使い方

  1. ファイルを保存してブラウザで開く
  2. 4問解いたあとに自動採点される
  3. 終了後に「棒グラフ📊」が表示
  4. 履歴は localStorage に蓄積され、次回も残ります

3テーマ対応版(数値リテラル/テンプレート文字列/エスケープシーケンス)

アプリ全体のコンセプト

テーマ学習内容例題の狙い
1. 数値リテラル10進・2進・16進・指数表記など0b10101.23e4 の動作理解
2. テンプレート文字列`${}` の展開や複数行文字列文字列結合との違い・改行の扱い
3. エスケープシーケンス\n, \\, \', \u{} などバグを修正して正しい出力を得る

コード全体(テーマ切り替えつき)

以下を string_practice.html として保存・実行すればOK。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>JavaScript文字列演習アプリ</title>
<style>
  body { font-family: "Segoe UI", sans-serif; padding: 2rem; background: #f7f9fc; }
  h1 { color: #333; }
  select, textarea, button { font-size: 16px; margin-top: 10px; }
  pre { background: #f3f3f3; padding: 1em; border-radius: 8px; }
  textarea { width: 100%; height: 120px; font-family: monospace; }
  button { padding: 8px 16px; border-radius: 6px; cursor: pointer; }
  #result { margin-top: 1em; font-weight: bold; }
  .correct { color: green; }
  .wrong { color: crimson; }
  .hint { color: #555; font-style: italic; }
  #progress { margin-top: 10px; font-size: 14px; }
</style>
</head>
<body>

<h1>🧩 JavaScript文字列演習アプリ</h1>
<p>テーマを選んで「問題開始」ボタンを押してください。</p>

<label for="theme">テーマ:</label>
<select id="theme">
  <option value="escape">エスケープシーケンス</option>
  <option value="number">数値リテラル</option>
  <option value="template">テンプレート文字列</option>
</select>
<button id="startBtn">問題開始</button>

<hr>
<pre id="question"></pre>
<textarea id="answer"></textarea><br />
<button id="runBtn" style="display:none;">実行</button>
<div id="result"></div>
<div id="progress"></div>

<script>
// ===== 各テーマの問題セット =====
const data = {
  escape: [
    { q: "console.log('It\\'s a pen.');", expect: "It's a pen.", hint: "シングルクォート内の ' をエスケープ。" },
    { q: "console.log('C:\\\\Users\\\\Halu');", expect: "C:\\Users\\Halu", hint: "バックスラッシュは2つ。" },
    { q: "console.log('Hello,\\nWorld!');", expect: "Hello,\nWorld!", hint: "\\n で改行。" },
    { q: "console.log('\\u{1F600}');", expect: "😀", hint: "Unicode絵文字は \\u{1F600}。" }
  ],
  number: [
    { q: "console.log(0b1010);", expect: 10, hint: "0b は2進数。" },
    { q: "console.log(0x1A);", expect: 26, hint: "0x は16進数。" },
    { q: "console.log(1.23e2);", expect: 123, hint: "e は10の指数。" },
    { q: "console.log(Number('10'));", expect: 10, hint: "文字列→数値変換。" }
  ],
  template: [
    { q: "const name = 'Halu'; console.log(`Hello, ${name}!`);", expect: "Hello, Halu!", hint: "テンプレートリテラルは `${}` 展開。" },
    { q: "const a=2,b=3; console.log(`${a}+${b}=${a+b}`);", expect: "2+3=5", hint: "式の結果を展開できる。" },
    { q: "console.log(`Line1\\nLine2`);", expect: "Line1\nLine2", hint: "改行文字を理解しよう。" },
    { q: "console.log(`ABC\nDEF`);", expect: "ABC\nDEF", hint: "テンプレート内では改行もOK。" }
  ]
};

let current = 0, correct = 0, currentSet = [];

const qEl = document.getElementById("question");
const aEl = document.getElementById("answer");
const rEl = document.getElementById("result");
const pEl = document.getElementById("progress");
const runBtn = document.getElementById("runBtn");

document.getElementById("startBtn").onclick = () => {
  const theme = document.getElementById("theme").value;
  currentSet = data[theme];
  current = 0; correct = 0;
  runBtn.style.display = "inline-block";
  loadQuestion();
};

function loadQuestion() {
  const q = currentSet[current];
  qEl.textContent = q.q;
  aEl.value = q.q;
  rEl.textContent = "";
  pEl.textContent = `進捗:${current+1}/${currentSet.length}`;
}

runBtn.onclick = () => {
  const q = currentSet[current];
  try {
    const output = eval(aEl.value);
    if (output === q.expect) {
      rEl.innerHTML = "✅ 正解!";
      rEl.className = "correct";
      correct++;
      nextQuestion();
    } else {
      rEl.innerHTML = `❌ 出力が違います。<div class='hint'>ヒント:${q.hint}</div>`;
      rEl.className = "wrong";
    }
  } catch(e) {
    rEl.innerHTML = `⚠️ エラー: ${e.message}<div class='hint'>ヒント:${q.hint}</div>`;
    rEl.className = "wrong";
  }
};

function nextQuestion() {
  current++;
  if (current < currentSet.length) {
    setTimeout(loadQuestion, 1500);
  } else {
    rEl.innerHTML = `🎉 全問完了! 正答数:${correct}/${currentSet.length}`;
    runBtn.style.display = "none";
  }
}
</script>
</body>
</html>
HTML

使い方

  1. ブラウザで開くと「テーマ選択」画面が出ます
  2. テーマを選んで「問題開始」
  3. 表示されたコードを修正 → 「実行」
  4. 結果が正しければ自動で次へ
  5. 最後に正答数が表示されます
タイトルとURLをコピーしました