Skip to content

Instantly share code, notes, and snippets.

@nabily4e-dev
Created April 13, 2023 13:36
Show Gist options
  • Save nabily4e-dev/dce63550c482cfc7b2ab4791b786dbf5 to your computer and use it in GitHub Desktop.
Save nabily4e-dev/dce63550c482cfc7b2ab4791b786dbf5 to your computer and use it in GitHub Desktop.
<!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