Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions QUICKSTART.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Quick Start Guide

## 🚀 Get Started in 3 Steps

### Step 1: Clone the Repository
```bash
git clone https://github.com/leonardomso/33-js-concepts.git
cd 33-js-concepts
```

### Step 2: Start the Server
```bash
node server.js
```

Or using npm:
```bash
npm start
```

### Step 3: Open Your Browser
Navigate to: **http://localhost:3000**

That's it! 🎉

## 🎯 What You Can Do

- **Browse Concepts**: Click on any of the 33 JavaScript concepts in the sidebar
- **Search**: Use the search box to quickly find specific topics
- **Write Code**: Modify the example code in the editor
- **Run Code**: Click the "▶ Run Code" button (or press Ctrl/Cmd + Enter)
- **See Results**: View the console output below the editor
- **Reset**: Click "↻ Reset" to restore the original example
- **Clear**: Click "🗑️ Clear" to start with a blank editor

## 🎓 Learning Tips

1. Start with **Concept #1 (Call Stack)** and work your way through
2. Modify the example code to experiment
3. Try breaking things to understand error messages
4. Create your own examples for each concept
5. Use the console output to debug and learn

## ⚡ Keyboard Shortcuts

- `Ctrl/Cmd + Enter` - Run the code
- `Tab` - Insert 4 spaces (in the editor)

## 🔧 Requirements

- Node.js installed on your system
- A modern web browser (Chrome, Firefox, Safari, Edge)

## 📱 Mobile Friendly

The interface is fully responsive and works on mobile devices too!

## 🆘 Need Help?

- Check the [README.md](README.md) for more detailed information
- Visit the [main repository](https://github.com/leonardomso/33-js-concepts) for resources
- Open an issue if you encounter any problems

Happy Learning! 🎉
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<div align="center">
<p>
<a href="#introduction">Introduction</a> •
<a href="#interactive-learning">Interactive Learning</a> •
<a href="#community">Community</a> •
<a href="#table-of-contents">Table of Contents</a> •
<a href="#license">License</a>
Expand All @@ -24,6 +25,53 @@

This repository was created with the intention of helping developers master their concepts in JavaScript. It is not a requirement, but a guide for future studies. It is based on an article written by Stephen Curtis and you can read it [here](https://medium.com/@stephenthecurt/33-fundamentals-every-javascript-developer-should-know-13dd720a90d1).

## 🎮 Interactive Learning

**NEW!** Now you can learn and test JavaScript concepts interactively! We've added a plug-and-play GUI that lets you:

- 📚 Browse all 33 JavaScript concepts in an organized list
- 💻 Write and test code in a live editor
- 🔍 Search for specific concepts
- ▶️ Execute JavaScript code directly in your browser
- 📋 See real-time console output
- 🎯 Learn by doing with pre-loaded examples

### Getting Started with the Interactive GUI

1. **Clone this repository:**
```bash
git clone https://github.com/leonardomso/33-js-concepts.git
cd 33-js-concepts
```

2. **Start the local server:**
```bash
node server.js
```

Or using npm:
```bash
npm start
```

3. **Open your browser:**
Navigate to `http://localhost:3000` to start learning!

### Features

- ✨ **Clean, Modern Interface**: Beautiful UI with smooth interactions
- 🔥 **Live Code Editor**: Write and test JavaScript code instantly
- 📱 **Responsive Design**: Works on desktop, tablet, and mobile
- 🎨 **Syntax-Friendly**: Comfortable code editing experience
- 🚀 **Zero Dependencies**: Pure HTML, CSS, and JavaScript - no build tools needed
- ⚡ **Instant Feedback**: See your code results immediately
- 🎓 **Educational Examples**: Each concept comes with working code examples

### Keyboard Shortcuts

- `Ctrl/Cmd + Enter` - Run code
- `Tab` - Insert 4 spaces in editor

## Community

Feel free to submit a PR by adding a link to your own recaps or reviews. If you want to translate the repo into your native language, please feel free to do so.
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
},
"homepage": "https://github.com/leonardomso/33#readme",
"scripts": {
"start": "node server.js",
"dev": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
Expand Down
248 changes: 248 additions & 0 deletions public/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
// State management
let currentConcept = null;
let originalCode = '';

// DOM elements
const conceptsList = document.getElementById('conceptsList');
const conceptTitle = document.getElementById('conceptTitle');
const conceptDescription = document.getElementById('conceptDescription');
const codeEditor = document.getElementById('codeEditor');
const output = document.getElementById('output');
const searchInput = document.getElementById('searchInput');
const runBtn = document.getElementById('runBtn');
const clearBtn = document.getElementById('clearBtn');
const resetBtn = document.getElementById('resetBtn');
const clearOutputBtn = document.getElementById('clearOutputBtn');

// Initialize the application
function init() {
renderConceptsList();
setupEventListeners();

// Load first concept by default
if (conceptsData.length > 0) {
selectConcept(conceptsData[0].id);
}
}

// Render the concepts list
function renderConceptsList(filter = '') {
conceptsList.innerHTML = '';

const filteredConcepts = conceptsData.filter(concept =>
concept.title.toLowerCase().includes(filter.toLowerCase())
);

filteredConcepts.forEach(concept => {
const li = document.createElement('li');
li.innerHTML = `
<span class="concept-number">${concept.id}</span>
${concept.title}
`;
li.addEventListener('click', () => selectConcept(concept.id));

if (currentConcept && currentConcept.id === concept.id) {
li.classList.add('active');
}

conceptsList.appendChild(li);
});
}

// Select and load a concept
function selectConcept(conceptId) {
const concept = conceptsData.find(c => c.id === conceptId);
if (!concept) return;

currentConcept = concept;
originalCode = concept.example;

// Update UI
conceptTitle.textContent = `${concept.id}. ${concept.title}`;
conceptDescription.textContent = concept.description;
codeEditor.value = concept.example;

// Clear output
clearOutput();

// Update active state in list
renderConceptsList(searchInput.value);

// Scroll to top of content
window.scrollTo({ top: 0, behavior: 'smooth' });
}

// Setup event listeners
function setupEventListeners() {
// Search functionality
searchInput.addEventListener('input', (e) => {
renderConceptsList(e.target.value);
});

// Run code button
runBtn.addEventListener('click', runCode);

// Clear editor button
clearBtn.addEventListener('click', () => {
codeEditor.value = '';
clearOutput();
});

// Reset to original code
resetBtn.addEventListener('click', () => {
if (originalCode) {
codeEditor.value = originalCode;
clearOutput();
}
});

// Clear output button
clearOutputBtn.addEventListener('click', clearOutput);

// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Ctrl/Cmd + Enter to run code
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
e.preventDefault();
runCode();
}
});

// Allow Tab key in textarea
codeEditor.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
e.preventDefault();
const start = codeEditor.selectionStart;
const end = codeEditor.selectionEnd;

codeEditor.value = codeEditor.value.substring(0, start) +
' ' +
codeEditor.value.substring(end);

codeEditor.selectionStart = codeEditor.selectionEnd = start + 4;
}
});
}

// Run the code in the editor
function runCode() {
const code = codeEditor.value.trim();

if (!code) {
addOutput('No code to execute', 'error');
return;
}

clearOutput();

// Capture console output
const originalConsole = {
log: console.log,
error: console.error,
warn: console.warn,
info: console.info
};

const logs = [];

// Override console methods
console.log = (...args) => {
const message = args.map(arg => formatValue(arg)).join(' ');
logs.push({ type: 'log', message });
originalConsole.log(...args);
};

console.error = (...args) => {
const message = args.map(arg => formatValue(arg)).join(' ');
logs.push({ type: 'error', message });
originalConsole.error(...args);
};

console.warn = (...args) => {
const message = args.map(arg => formatValue(arg)).join(' ');
logs.push({ type: 'warn', message });
originalConsole.warn(...args);
};

console.info = (...args) => {
const message = args.map(arg => formatValue(arg)).join(' ');
logs.push({ type: 'info', message });
originalConsole.info(...args);
};

try {
// Execute the code
const result = eval(code);

// Display logs
logs.forEach(log => {
addOutput(log.message, log.type);
});

// If there's a return value and no logs, display it
if (result !== undefined && logs.length === 0) {
addOutput(`=> ${formatValue(result)}`, 'success');
}

// If no output at all
if (logs.length === 0 && result === undefined) {
addOutput('Code executed successfully (no output)', 'success');
}

} catch (error) {
addOutput(`Error: ${error.message}`, 'error');
console.error(error);
} finally {
// Restore console methods
console.log = originalConsole.log;
console.error = originalConsole.error;
console.warn = originalConsole.warn;
console.info = originalConsole.info;
}
}

// Format values for display
function formatValue(value) {
if (value === null) return 'null';
if (value === undefined) return 'undefined';
if (typeof value === 'string') return value;
if (typeof value === 'function') return value.toString();
if (typeof value === 'symbol') return value.toString();

try {
return JSON.stringify(value, null, 2);
} catch (e) {
return String(value);
}
}

// Add output to the console
function addOutput(message, type = 'log') {
const outputDiv = document.getElementById('output');

// Remove empty state
outputDiv.classList.remove('empty');

const logEntry = document.createElement('div');
logEntry.className = `log ${type}`;
logEntry.textContent = message;

outputDiv.appendChild(logEntry);

// Auto-scroll to bottom
outputDiv.scrollTop = outputDiv.scrollHeight;
}

// Clear the output console
function clearOutput() {
output.innerHTML = '';
output.classList.add('empty');
output.textContent = 'Run your code to see output here...';
}

// Initialize the application when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
Loading