Student starter code (30% baseline)
index.html
- Main HTML pagescript.js
- JavaScript logicstyles.css
- Styling and layoutpackage.json
- Dependenciessetup.sh
- Setup scriptREADME.md
- Instructions (below)๐ก Download the ZIP, extract it, and follow the instructions below to get started!
A comprehensive StackBlitz template for building dynamic comparison interfaces using multiple APIs and Promise.all() for parallel data fetching.
Compare Pokemon across multiple battle statistics:
Compare characters from the Star Wars universe:
Compare real-time weather across multiple cities:
Compare countries and their demographics:
activity-06-comparison/
โโโ index.html # Main comparison dashboard interface
โโโ styles.css # Advanced responsive grid layouts
โโโ script.js # Parallel API fetching and comparison logic
โโโ README.md # This comprehensive documentation
โโโ sample-data.json # Fallback data and testing scenarios
python3 -m http.server 8001
http://localhost:8001
async function getMultipleItems(itemNames) {
try {
console.log(`๐ฅ Fetching ${itemNames.length} items simultaneously...`);
// Create array of fetch promises
const fetchPromises = itemNames.map(name =>
fetch(`${API_BASE}/${name.toLowerCase()}`)
.then(response => {
if (!response.ok) throw new Error(`${name} not found`);
return response.json();
})
);
// Wait for all promises to complete
const itemData = await Promise.all(fetchPromises);
console.log('โ
All items loaded successfully!');
return itemData;
} catch (error) {
console.error('โ Failed to fetch items:', error);
throw error;
}
}
function analyzeComparison(items) {
const stats = {
count: items.length,
categories: {}
};
// Find strongest in each category
if (comparisonType === 'pokemon') {
const attacks = items.map(p => getStatValue(p, 'attack'));
const defenses = items.map(p => getStatValue(p, 'defense'));
stats.strongest = {
attacker: items[attacks.indexOf(Math.max(...attacks))],
defender: items[defenses.indexOf(Math.max(...defenses))]
};
}
// Calculate averages and insights
stats.averages = calculateAverages(items);
return stats;
}
.comparison-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 25px;
padding: 40px;
}
.comparison-card {
background: linear-gradient(145deg, #ffffff, #f8f9fa);
border-radius: 15px;
transition: all 0.3s ease;
animation: slideIn 0.5s ease;
}
.comparison-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0,0,0,0.15);
}
https://pokeapi.co/api/v2/pokemon/{name}
https://swapi.dev/api/people/{id}/
https://api.openweathermap.org/data/2.5/weather
https://restcountries.com/v3.1/name/{name}
/* Responsive comparison grid */
.comparison-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 25px;
}
/* Card hover effects */
.comparison-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0,0,0,0.15);
border-color: #667eea;
}
/* Loading animations */
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255,255,255,0.3);
transition: width 0.3s ease, height 0.3s ease;
transform: translate(-50%, -50%);
}
.btn:hover::before {
width: 300px;
height: 300px;
}
// Test different API response formats
const testScenarios = [
{ type: 'pokemon', item: 'pikachu', expected: 'object' },
{ type: 'starwars', item: '1', expected: 'object' },
{ type: 'weather', item: 'London', expected: 'object' },
{ type: 'countries', item: 'Japan', expected: 'array' }
];
function calculatePokemonBattleScore(pokemon) {
const attack = getStatValue(pokemon, 'attack');
const defense = getStatValue(pokemon, 'defense');
const speed = getStatValue(pokemon, 'speed');
const hp = getStatValue(pokemon, 'hp');
// Weighted scoring system
return (attack * 1.2) + (defense * 1.0) + (speed * 1.1) + (hp * 0.8);
}
function compareCharacters(char1, char2) {
const metrics = {
physical: comparePhysicalStats(char1, char2),
experience: compareBirthYear(char1, char2),
origin: compareHomeworld(char1, char2)
};
return generateComparisonInsights(metrics);
}
const API_CONFIGS = {
pokemon: {
baseUrl: 'https://pokeapi.co/api/v2/pokemon',
exampleItems: ['pikachu', 'charmander', 'squirtle'],
maxItems: 6,
cacheTimeout: 300000 // 5 minutes
},
weather: {
baseUrl: 'https://api.openweathermap.org/data/2.5/weather',
apiKey: 'your_api_key_here',
units: 'metric',
exampleItems: ['London', 'Paris', 'Tokyo']
}
};
// Implement intelligent caching
const apiCache = new Map();
async function fetchWithCache(url, cacheKey) {
if (apiCache.has(cacheKey)) {
const cached = apiCache.get(cacheKey);
if (Date.now() - cached.timestamp < 300000) {
return cached.data;
}
}
const data = await fetch(url).then(r => r.json());
apiCache.set(cacheKey, { data, timestamp: Date.now() });
return data;
}
Issue: CORS errors when testing locally Solution: Use StackBlitz or enable CORS in development server
Issue: Rate limiting with weather API Solution: Implement caching and request throttling
Issue: Pokemon names not found Solution: Convert to lowercase and handle special characters
Issue: Slow loading with multiple items Solution: Use Promise.all() for parallel requests
Issue: UI becomes unresponsive Solution: Implement loading states and optimize rendering
This template teaches essential web development concepts:
This educational template can be enhanced in many ways:
Build amazing comparison dashboards! ๐ This template demonstrates the power of modern web APIs and provides a solid foundation for creating data-driven applications that engage users with interactive comparisons and insights.