-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
148 lines (134 loc) · 6.13 KB
/
script.js
File metadata and controls
148 lines (134 loc) · 6.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// =========================================================================
// JavaScript Functions — Scope, Parameters & Return Values
// This section defines reusable functions that demonstrate the key concepts.
// =========================================================================
/**
* Computes an animation duration (in seconds) given a distance and speed.
* This demonstrates the use of parameters and returning a calculated value.
* @param {number} distance - The distance the element should travel in pixels.
* @param {number} speed - The speed of the element in pixels per second.
* @returns {number} The calculated duration in seconds (precision: 2 decimals).
*/
function computeDuration(distance, speed){
// Ensure speed is not zero to avoid division by zero
const s = Math.max(0.1, distance / Math.max(1, speed));
return Math.round(s * 100) / 100;
}
/**
* Apply the computed duration to the CSS variable of the box.
* @param {HTMLElement} box - The HTML element whose CSS variable will be updated.
* @param {number} seconds - The duration in seconds.
* @returns {string} - A CSS-formatted time string (e.g., "0.7s"), demonstrating a return value.
*/
function setBoxDuration(box, seconds){
const time = `${seconds}s`;
box.style.setProperty('--duration', time);
return time; // Return the string for use elsewhere
}
/**
* A higher-order function that creates and returns a new function.
* This is an advanced demonstration of scope, known as a 'closure'.
* The inner function 'remembers' the 'box' and 'current' variables even after 'makeBoxAnimator' has finished executing.
* @param {"left"|"right"} direction
* @param {number} distance
* @returns {function} A function that animates the box.
*/
function makeBoxAnimator(box){
// The 'current' variable belongs to the local/closed-over scope.
// It maintains the state of the box's position between calls.
let current = 0; // local (closure) state
return function animate(direction, distance){
const dir = direction === 'left' ? -1 : 1;
current += dir * distance;
box.style.transform = `translateX(${current}px)`;
return current; // Return the new position
};
}
// ------ Execute when the DOM is ready ------
document.addEventListener('DOMContentLoaded', () => {
// Select all necessary DOM elements
const root = document.documentElement;
const box = document.getElementById('box');
const distanceInput = document.getElementById('distanceInput');
const speedInput = document.getElementById('speedInput');
const animateRightBtn = document.getElementById('animateRight');
const animateLeftBtn = document.getElementById('animateLeft');
const durationOut = document.getElementById('durationOut');
const flipCard = document.getElementById('flipCard');
const modal = document.getElementById('modal');
const openModalBtn = document.getElementById('openModal');
const closeModalBtn = document.getElementById('closeModal');
const loader = document.getElementById('loader');
const toggleLoaderBtn = document.getElementById('toggleLoader');
const themeToggle = document.getElementById('themeToggle');
// Bonus feature: Theme toggle (global scope var: 'root' used by inner function)
function toggleTheme(){
const isLight = root.classList.toggle('light');
localStorage.setItem('theme', isLight ? 'light' : 'dark');
return isLight;
}
themeToggle.addEventListener('click', toggleTheme);
// Initialize theme from storage
if(localStorage.getItem('theme') === 'light'){ root.classList.add('light'); }
// Animator setup (closure demonstration)
const animateBox = makeBoxAnimator(box);
// Helper to read inputs safely (local scope example)
function readParams(){
// 'distance' and 'speed' are local variables, existing only here.
const distance = Math.max(1, Number(distanceInput.value || 0));
const speed = Math.max(1, Number(speedInput.value || 1));
return { distance, speed };
}
// Function that combines calculation and animation logic
function updateAndAnimate(direction){
const { distance, speed } = readParams();
const seconds = computeDuration(distance, speed);
const timeString = setBoxDuration(box, seconds);
durationOut.textContent = `Duration: ${timeString} (distance: ${distance}px, speed: ${speed}px/s)`;
animateBox(direction, distance);
}
animateRightBtn.addEventListener('click', () => updateAndAnimate('right'));
animateLeftBtn.addEventListener('click', () => updateAndAnimate('left'));
// ===================================================
// Combining CSS & JS
// We use JavaScript to add/remove classes,
// triggering the animations defined in the CSS.
// ===================================================
// Flip card logic
function flip(){
flipCard.classList.toggle('is-flipped'); // Triggers the flip animation
const pressed = flipCard.classList.contains('is-flipped');
flipCard.setAttribute('aria-pressed', String(pressed));
return pressed;
}
flipCard.addEventListener('click', flip);
flipCard.addEventListener('keyup', (e) => { if(e.key === 'Enter' || e.key === ' ') flip(); });
// Modal logic
function openModal(){
modal.setAttribute('aria-hidden', 'false'); // Triggers the modal's entrance animation
}
function closeModal(){
modal.setAttribute('aria-hidden', 'true');
}
openModalBtn.addEventListener('click', openModal);
closeModalBtn.addEventListener('click', closeModal);
modal.addEventListener('click', (e)=>{ if(e.target === modal) closeModal(); }); // Close if clicking outside
document.addEventListener('DOMContentLoaded', () => {
const modal=document.getElementById('modal');
const closeModal=()=>modal.setAttribute('aria-hidden','true');
const toggleLoaderBtn={addEventListener:()=>{}};
});
// Close modal with Escape key (accessibility bonus)
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.getAttribute('aria-hidden') === 'false') {
closeModal();
}
});
// Loader logic
function toggleLoader(){
const on = loader.classList.toggle('is-on');
loader.setAttribute('aria-hidden', String(!on));
return on;
}
toggleLoaderBtn.addEventListener('click', toggleLoader);
});