Created
April 13, 2023 13:36
-
-
Save nabily4e-dev/dce63550c482cfc7b2ab4791b786dbf5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<title>Sparrow Photography</title> | |
<meta | |
name="viewport" | |
content="width=device-width, initial-scale=1.0" | |
/> | |
<style> | |
body { | |
margin: 0 auto; | |
max-width: 50em; | |
width: 88%; | |
} | |
/** | |
* Grid Layout | |
*/ | |
@media (min-width: 20em) { | |
#photos { | |
display: grid; | |
grid-template-columns: repeat(2, 1fr); | |
grid-template-rows: 1fr; | |
grid-column-gap: 2em; | |
grid-row-gap: 2em; | |
} | |
} | |
@media (min-width: 32em) { | |
#photos { | |
grid-template-columns: repeat(3, 1fr); | |
} | |
} | |
@media (min-width: 42em) { | |
#photos { | |
grid-template-columns: repeat(4, 1fr); | |
} | |
} | |
/** | |
* Nav Menu | |
*/ | |
.nav { | |
padding: 1em 0; | |
} | |
.nav a { | |
text-decoration: none; | |
} | |
.nav a:focus, | |
.nav a:hover { | |
text-decoration: underline; | |
} | |
/** | |
* Footer | |
*/ | |
footer { | |
border-top: 1px solid #e5e5e5; | |
margin-top: 5em; | |
} | |
/** | |
* Responsive images | |
*/ | |
img { | |
height: auto; | |
max-width: 100%; | |
} | |
/** | |
* Spinner | |
*/ | |
.spinner { | |
width: 40px; | |
height: 40px; | |
margin: 0 auto; | |
position: relative; | |
} | |
.spinner:before { | |
content: ''; | |
display: block; | |
position: absolute; | |
width: 20px; | |
height: 20px; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
margin: auto; | |
border: 2px solid transparent; | |
border-top-color: #555; | |
border-radius: 50%; | |
animation: spin 1s ease infinite; | |
} | |
@keyframes spin { | |
from { | |
transform: rotate(0deg); | |
} | |
to { | |
transform: rotate(360deg); | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<nav class="nav"> | |
<a | |
class="logo" | |
href="index.html" | |
><strong>Sparrow Photography</strong></a | |
> | |
</nav> | |
<h1>Sparrow Photography</h1> | |
<div id="app"></div> | |
<div id="photos"></div> | |
<footer> | |
<p><em>Photos by Jack Sparrow. All rights reserved.</em></p> | |
</footer> | |
<script> | |
// This script fetches and displays photos from an API | |
// Use strict mode to avoid errors | |
'use strict' | |
/** | |
* A state object to store the photos and loading status | |
* @type {Object} | |
* @property {Array} photos - The array of photos | |
* @property {boolean} loading - Whether or not the data is still loading | |
*/ | |
const state = { | |
photos: [], // The array of photos | |
loading: true, // Whether or not the data is still loading | |
} | |
// Get the app and photos elements from the document | |
const appEl = document.getElementById('app') | |
const photosEl = document.getElementById('photos') | |
/** | |
* A function to update the UI based on the state | |
*/ | |
function render() { | |
if (state.loading) { | |
// Display a loading spinner | |
setTimeout(() => { | |
photosEl.innerHTML = '<div class="spinner"></div>' | |
}, 100) | |
} else { | |
// Display the photos | |
const photoElements = state.photos | |
.map( | |
(photo) => ` | |
<div> | |
<img src="${photo.url}" alt="${photo.name}" /> | |
<div> | |
<h2>${photo.name}</h2> | |
<p>${photo.description}</p> | |
<p>${photo.price}</p> | |
</div> | |
</div> | |
` | |
) | |
.join('') | |
photosEl.innerHTML = photoElements | |
} | |
} | |
/** | |
* A function to update the state and call the render function | |
* @param {Object} newState - The new state object to merge with the current state | |
*/ | |
function setState(newState) { | |
Object.assign(state, newState) | |
render() | |
} | |
/** | |
* An async function to fetch photos from the API and update the state | |
* @returns {Promise<void>} - A promise that resolves when the photos are fetched and displayed | |
*/ | |
async function fetchPhotos() { | |
try { | |
const response = await fetch( | |
'https://vanillajsacademy.com/api/photos.json' | |
) | |
const data = await response.json() | |
setState({ | |
photos: data, | |
loading: false, | |
}) | |
} catch (error) { | |
// Handle errors gracefully | |
appEl.innerHTML = '<p>Failed to load photos.</p>' | |
console.error(error) | |
} | |
} | |
// Call the fetchPhotos function to start the process | |
fetchPhotos() | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment