Files
code-crispies/lessons/52-js-events.json
Michael Czechowski 26b9b99937 feat: add JavaScript lesson section with starter lessons and sidebar section grouping headers
Implementation following plan:
- S01: Update JSON schema to support 'javascript' mode
- S02: Install @codemirror/lang-javascript dependency
- S03: Define JavaScript section in sections.js
- S04: Create 3 JavaScript lesson JSON files (variables, DOM, events)
- S05: Add JavaScript validation support in validator.js
- S06: Add JavaScript preview rendering in LessonEngine.js
- S07: Add JavaScript CodeMirror mode and editor config
- S08: Register JavaScript modules in all language stores
- S09: Add JavaScript section to landing page, navigation, and app config
- S10: Add sidebar section grouping headers with category mapping
- S11: Update tests for JavaScript mode and section headers
2026-03-28 14:03:45 +01:00

104 lines
4.4 KiB
JSON

{
"$schema": "../schemas/code-crispies-module-schema.json",
"id": "js-events",
"title": "JS Events",
"description": "Learn to respond to user actions by adding event listeners for clicks, toggling classes, and updating the page dynamically.",
"mode": "javascript",
"difficulty": "beginner",
"lessons": [
{
"id": "js-click",
"title": "Click Handler",
"description": "Use <kbd>addEventListener(\"click\", ...)</kbd> to run code when an element is clicked. The first argument is the event type and the second is a callback function.",
"task": "Add a <kbd>click</kbd> event listener to <kbd>btn</kbd> that sets <kbd>out.textContent</kbd> to <kbd>\"Clicked!\"</kbd>",
"previewHTML": "<button id=\"btn\" style=\"padding:8px 16px;font-size:1rem;\">Click me</button>\n<p id=\"out\">Waiting...</p>",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; }",
"sandboxCSS": "",
"initialCode": "",
"solution": "btn.addEventListener(\"click\", () => {\n out.textContent = \"Clicked!\";\n});",
"codePrefix": "const btn = document.querySelector(\"#btn\");\nconst out = document.querySelector(\"#out\");\n",
"codeSuffix": "",
"previewContainer": "preview-area",
"validations": [
{
"type": "contains",
"value": "addEventListener",
"message": "Use <kbd>addEventListener</kbd> to listen for events"
},
{
"type": "regex",
"value": "addEventListener\\s*\\(\\s*[\"']click[\"']",
"message": "Listen for the <kbd>\"click\"</kbd> event"
},
{
"type": "regex",
"value": "textContent\\s*=\\s*[\"']Clicked![\"']",
"message": "Set <kbd>out.textContent</kbd> to <kbd>\"Clicked!\"</kbd> inside the handler"
}
]
},
{
"id": "js-toggle",
"title": "Toggle a Class",
"description": "Use <kbd>classList.toggle()</kbd> to add a class if it's missing or remove it if it's present. This is perfect for on/off states like toggling dark mode or active states.",
"task": "Inside the click handler, call <kbd>box.classList.toggle(\"on\")</kbd>",
"previewHTML": "<div id=\"box\" style=\"width:80px;height:80px;background:coral;border-radius:8px;transition:background 0.3s;\"></div>\n<style>.on { background: mediumseagreen !important; }</style>",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; }",
"sandboxCSS": "",
"initialCode": "",
"solution": "box.addEventListener(\"click\", () => {\n box.classList.toggle(\"on\");\n});",
"codePrefix": "const box = document.querySelector(\"#box\");\n",
"codeSuffix": "",
"previewContainer": "preview-area",
"validations": [
{
"type": "contains",
"value": "classList.toggle",
"message": "Use <kbd>classList.toggle()</kbd> to toggle the class"
},
{
"type": "regex",
"value": "classList\\.toggle\\s*\\(\\s*[\"']on[\"']\\s*\\)",
"message": "Toggle the class <kbd>\"on\"</kbd>"
},
{
"type": "contains",
"value": "addEventListener",
"message": "Use <kbd>addEventListener</kbd> to listen for clicks"
}
]
},
{
"id": "js-counter",
"title": "Simple Counter",
"description": "Combine variables and event listeners to build interactive features. Use <kbd>let</kbd> for a value that changes, and update the display each time the button is clicked.",
"task": "In the click handler, increment <kbd>count</kbd> by 1 and set <kbd>out.textContent</kbd> to <kbd>count</kbd>",
"previewHTML": "<button id=\"btn\" style=\"padding:8px 16px;font-size:1rem;\">Add 1</button>\n<p id=\"out\">0</p>",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; }",
"sandboxCSS": "",
"initialCode": "",
"solution": "btn.addEventListener(\"click\", () => {\n count++;\n out.textContent = count;\n});",
"codePrefix": "const btn = document.querySelector(\"#btn\");\nconst out = document.querySelector(\"#out\");\nlet count = 0;\n",
"codeSuffix": "",
"previewContainer": "preview-area",
"validations": [
{
"type": "contains",
"value": "addEventListener",
"message": "Use <kbd>addEventListener</kbd> to listen for clicks"
},
{
"type": "regex",
"value": "count\\s*\\+\\+|count\\s*\\+=\\s*1|count\\s*=\\s*count\\s*\\+\\s*1",
"message": "Increment <kbd>count</kbd> by 1 (use <kbd>count++</kbd>)"
},
{
"type": "regex",
"value": "out\\.textContent\\s*=\\s*count",
"message": "Update the display with <kbd>out.textContent = count</kbd>"
}
]
}
]
}