Skip to content

Commit 21024c5

Browse files
authored
feat: adds Dean Lesson 20 resources and stretch (#676)
* feat: adds Dean lesson 20 resources including images, HTML, CSS, and JavaScript for interactive components * feat: implement looping functionality for gallery navigation
1 parent c7908f6 commit 21024c5

File tree

10 files changed

+1222
-0
lines changed

10 files changed

+1222
-0
lines changed
11.3 KB
Loading
10.7 KB
Loading

lesson_20/deanwalston/lotr.png

13.9 KB
Loading
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>Dean Lesson 20 Web Development</title>
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<link rel="stylesheet" type="text/css" href="style.css">
7+
<script src="script.js" defer></script>
8+
9+
</head>
10+
<body>
11+
<header class="header">
12+
<h1>Medieval Wall of Fame</h1>
13+
</header>
14+
<div class="tab-container">
15+
<div class="tab-link active" onclick="showTab(0)">
16+
Kingdoms
17+
</div>
18+
<div class="tab-link" onclick="showTab(1)">
19+
Kings and Queens
20+
</div>
21+
<div class="tab-link" onclick="showTab(2)">
22+
Knights
23+
</div>
24+
</div>
25+
26+
<div class="tab-content-container">
27+
<div class="tab-content active" id="tab-0">
28+
<h2>Kingdoms</h2>
29+
<p>Powerful states ruled by a king or queen</p>
30+
<ul>
31+
<li>territorial units that controlled land and resources</li>
32+
<li>often engaged in wars with each other</li>
33+
<li>had a complex hierarchy of nobles</li>
34+
</ul>
35+
</div>
36+
<div class="tab-content" id="tab-1">
37+
<h2>Kings and Queens</h2>
38+
<p>Rulers of medieval kingdoms</p>
39+
<ul>
40+
<li>hereditary ruler during their time in leadership</li>
41+
<li>often married for political alliances</li>
42+
<li>held absolute power in their realms</li>
43+
</ul>
44+
</div>
45+
<div class="tab-content" id="tab-2">
46+
<h2>Knights</h2>
47+
<p>Brave warriors who fought for their lords</p>
48+
<ul>
49+
<li>swore loyalty to their king or queen</li>
50+
<li>followed a code of chivalry</li>
51+
<li>often fought in battles on foot or horseback</li>
52+
</ul>
53+
</div>
54+
</div>
55+
56+
<section class="photo-gallery-section">
57+
<h2>Medieval Wall of Fame Gallery</h2>
58+
<div class="gallery-container">
59+
<button class="gallery-nav gallery-prev" onclick="previousImages()" aria-label="Previous Images">
60+
<span>&lt;</span>
61+
</button>
62+
63+
<div class="gallery-viewport">
64+
<div class="gallery-track" id="gallery-track">
65+
<div class="gallery-item">
66+
<img src="lotr.png" alt="Lord of the Rings Scene">
67+
<div class="gallery-caption">The Fellowship of the Ring</div>
68+
</div>
69+
<div class="gallery-item">
70+
<img src="the_black_knight.png" alt="The Black Knight Scene">
71+
<div class="gallery-caption">The Black Knight</div>
72+
</div>
73+
<div class="gallery-item">
74+
<img src="the_starks_got.png" alt="Game of Thrones Starks">
75+
<div class="gallery-caption">House Stark - Game of Thrones</div>
76+
</div>
77+
<div class="gallery-item">
78+
<img src="king_army.png" alt="King Arthur: Legend of the Sword Scene">
79+
<div class="gallery-caption">King's Army</div>
80+
</div>
81+
<div class="gallery-item">
82+
<img src="war.png" alt="Kings of War Scene">
83+
<div class="gallery-caption">Army of Knights</div>
84+
</div>
85+
<div class="gallery-item">
86+
<img src="joan_of_arc.png" alt="Joan of Arc Scene">
87+
<div class="gallery-caption">Joan of Arc</div>
88+
</div>
89+
</div>
90+
</div>
91+
92+
<button class="gallery-nav gallery-next" onclick="nextImages()" aria-label="Next Images">
93+
<span>&gt;</span>
94+
</button>
95+
</div>
96+
97+
<div class="gallery-indicators">
98+
<span class="indicator active" onclick="goToSet(0)"></span>
99+
<span class="indicator" onclick="goToSet(1)"></span>
100+
<span class="indicator" onclick="goToSet(2)"></span>
101+
</div>
102+
103+
<div class="gallery-info">
104+
<p>Displaying <span id="current-images">1-3</span> of <span id="total-images">3</span> images</p>
105+
</div>
106+
</section>
107+
<div class="accordion-container">
108+
<div class="accordion-item">
109+
<button class="accordion-header">The Fellowship of the Ring</button>
110+
<div class="accordion-content">
111+
<p>An epic fantasy adventure following hobbits, elves, and men on their quest to destroy the One Ring and defeat the Dark Lord Sauron.</p>
112+
</div>
113+
</div>
114+
<div class="accordion-item">
115+
<button class="accordion-header">The Black Knight</button>
116+
<div class="accordion-content">
117+
<p>A fearsome warrior from Monty Python, known for his refusal to surrender even when severely outmatched in battle.</p>
118+
</div>
119+
</div>
120+
<div class="accordion-item">
121+
<button class="accordion-header">House Stark - Game of Thrones</button>
122+
<div class="accordion-content">
123+
<p>The noble house of Winterfell, known for their honor, loyalty, and the motto "Winter is Coming." Leaders of the North.</p>
124+
</div>
125+
</div>
126+
<div class="accordion-item">
127+
<button class="accordion-header">King's Army</button>
128+
<div class="accordion-content">
129+
<p>A mighty medieval army led by a king, showcasing the power and grandeur of royal military forces in battle.</p>
130+
</div>
131+
</div>
132+
<div class="accordion-item">
133+
<button class="accordion-header">Army of Knights</button>
134+
<div class="accordion-content">
135+
<p>Elite warriors bound by chivalry and honor, fighting with sword and shield in the great wars of the medieval era.</p>
136+
</div>
137+
</div>
138+
<div class="accordion-item">
139+
<button class="accordion-header">Joan of Arc</button>
140+
<div class="accordion-content">
141+
<p>The legendary French heroine who led her nation to victory against the English, claiming divine guidance in her holy mission.</p>
142+
</div>
143+
</div>
144+
</div>
145+
146+
<footer class="footer">
147+
<p>&copy; Dean code's Differently.</p>
148+
</footer>
149+
</body>
150+
</html>

lesson_20/deanwalston/script.js

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// JavaScript for Interactive Components
2+
3+
// Tab functionality
4+
function showTab(tabIndex) {
5+
// Get all tab links and content
6+
const tabLinks = document.querySelectorAll('.tab-link');
7+
const tabContents = document.querySelectorAll('.tab-content');
8+
9+
// Remove active class from all tabs and content
10+
tabLinks.forEach(link => link.classList.remove('active'));
11+
tabContents.forEach(content => content.classList.remove('active'));
12+
13+
// Add active class to selected tab and content
14+
tabLinks[tabIndex].classList.add('active');
15+
document.getElementById(`tab-${tabIndex}`).classList.add('active');
16+
}
17+
18+
// Accordion functionality
19+
document.addEventListener('DOMContentLoaded', function() {
20+
const accordionHeaders = document.querySelectorAll('.accordion-header');
21+
22+
accordionHeaders.forEach(header => {
23+
header.addEventListener('click', function() {
24+
const accordionItem = this.parentElement;
25+
const content = accordionItem.querySelector('.accordion-content');
26+
27+
// Toggle active class
28+
accordionItem.classList.toggle('active');
29+
30+
// Close other accordion items (optional - remove if you want multiple open)
31+
accordionHeaders.forEach(otherHeader => {
32+
if (otherHeader !== this) {
33+
otherHeader.parentElement.classList.remove('active');
34+
}
35+
});
36+
});
37+
});
38+
});
39+
40+
// Photo Gallery functionality
41+
let currentGallerySet = 0;
42+
let galleryImages = [];
43+
let imagesPerView = 3;
44+
45+
// Initialize gallery when page loads
46+
document.addEventListener('DOMContentLoaded', function() {
47+
initializeGallery();
48+
updateGalleryLayout();
49+
window.addEventListener('resize', updateGalleryLayout);
50+
});
51+
52+
function initializeGallery() {
53+
const galleryItems = document.querySelectorAll('.gallery-item');
54+
galleryImages = Array.from(galleryItems);
55+
56+
updateGalleryInfo();
57+
updateIndicators();
58+
updateNavigationButtons();
59+
}
60+
61+
function updateGalleryLayout() {
62+
const width = window.innerWidth;
63+
64+
// Determine images per view based on screen size
65+
if (width <= 480) {
66+
imagesPerView = 1;
67+
} else if (width <= 768) {
68+
imagesPerView = 2;
69+
} else {
70+
imagesPerView = 3;
71+
}
72+
73+
// Reset to first set if current set is out of bounds
74+
const maxSets = Math.ceil(galleryImages.length / imagesPerView);
75+
if (currentGallerySet >= maxSets) {
76+
currentGallerySet = 0;
77+
}
78+
79+
updateGalleryDisplay();
80+
updateGalleryInfo();
81+
updateIndicators();
82+
updateNavigationButtons();
83+
}
84+
85+
function updateGalleryDisplay() {
86+
const track = document.getElementById('gallery-track');
87+
if (!track) return;
88+
89+
const translateX = -(currentGallerySet * 100);
90+
track.style.transform = `translateX(${translateX}%)`;
91+
92+
// Update gallery item widths based on current view
93+
galleryImages.forEach(item => {
94+
if (imagesPerView === 1) {
95+
item.style.flex = '0 0 100%';
96+
} else if (imagesPerView === 2) {
97+
item.style.flex = '0 0 calc(50% - 10px)';
98+
} else {
99+
item.style.flex = '0 0 calc(33.333% - 14px)';
100+
}
101+
});
102+
}
103+
104+
function nextImages() {
105+
const maxSets = Math.ceil(galleryImages.length / imagesPerView);
106+
107+
// Loop back to beginning when reaching the end
108+
if (currentGallerySet < maxSets - 1) {
109+
currentGallerySet++;
110+
} else {
111+
currentGallerySet = 0; // Loop back to start
112+
}
113+
114+
updateGalleryDisplay();
115+
updateGalleryInfo();
116+
updateIndicators();
117+
updateNavigationButtons();
118+
}
119+
120+
function previousImages() {
121+
const maxSets = Math.ceil(galleryImages.length / imagesPerView);
122+
123+
// Loop to end when going back from beginning
124+
if (currentGallerySet > 0) {
125+
currentGallerySet--;
126+
} else {
127+
currentGallerySet = maxSets - 1; // Loop to last set
128+
}
129+
130+
updateGalleryDisplay();
131+
updateGalleryInfo();
132+
updateIndicators();
133+
updateNavigationButtons();
134+
}
135+
136+
function goToSet(setIndex) {
137+
const maxSets = Math.ceil(galleryImages.length / imagesPerView);
138+
139+
if (setIndex >= 0 && setIndex < maxSets) {
140+
currentGallerySet = setIndex;
141+
updateGalleryDisplay();
142+
updateGalleryInfo();
143+
updateIndicators();
144+
updateNavigationButtons();
145+
}
146+
}
147+
148+
function updateGalleryInfo() {
149+
const currentImagesSpan = document.getElementById('current-images');
150+
const totalImagesSpan = document.getElementById('total-images');
151+
152+
if (currentImagesSpan && totalImagesSpan) {
153+
const startImage = (currentGallerySet * imagesPerView) + 1;
154+
const endImage = Math.min(startImage + imagesPerView - 1, galleryImages.length);
155+
156+
currentImagesSpan.textContent = `${startImage}-${endImage}`;
157+
totalImagesSpan.textContent = galleryImages.length;
158+
}
159+
}
160+
161+
function updateIndicators() {
162+
const indicators = document.querySelectorAll('.indicator');
163+
const maxSets = Math.ceil(galleryImages.length / imagesPerView);
164+
165+
// Hide/show indicators based on number of sets needed
166+
indicators.forEach((indicator, index) => {
167+
if (index < maxSets) {
168+
indicator.style.display = 'block';
169+
indicator.classList.toggle('active', index === currentGallerySet);
170+
} else {
171+
indicator.style.display = 'none';
172+
}
173+
});
174+
}
175+
176+
function updateNavigationButtons() {
177+
const prevBtn = document.querySelector('.gallery-prev');
178+
const nextBtn = document.querySelector('.gallery-next');
179+
180+
// Since we now have looping, navigation buttons are never disabled
181+
if (prevBtn) {
182+
prevBtn.disabled = false;
183+
}
184+
185+
if (nextBtn) {
186+
nextBtn.disabled = false;
187+
}
188+
}
189+
190+
// Add keyboard navigation for accessibility
191+
document.addEventListener('keydown', function(e) {
192+
if (e.target.closest('.photo-gallery-section')) {
193+
if (e.key === 'ArrowLeft') {
194+
e.preventDefault();
195+
previousImages();
196+
} else if (e.key === 'ArrowRight') {
197+
e.preventDefault();
198+
nextImages();
199+
}
200+
}
201+
});
202+
203+
// Touch/swipe support for mobile
204+
let touchStartX = 0;
205+
let touchEndX = 0;
206+
207+
document.addEventListener('touchstart', function(e) {
208+
if (e.target.closest('.gallery-viewport')) {
209+
touchStartX = e.changedTouches[0].screenX;
210+
}
211+
});
212+
213+
document.addEventListener('touchend', function(e) {
214+
if (e.target.closest('.gallery-viewport')) {
215+
touchEndX = e.changedTouches[0].screenX;
216+
handleSwipe();
217+
}
218+
});
219+
220+
function handleSwipe() {
221+
const swipeThreshold = 50;
222+
const swipeLength = touchEndX - touchStartX;
223+
224+
if (Math.abs(swipeLength) > swipeThreshold) {
225+
if (swipeLength > 0) {
226+
previousImages(); // Swipe right
227+
} else {
228+
nextImages(); // Swipe left
229+
}
230+
}
231+
}

0 commit comments

Comments
 (0)