forked from Technigo/js-project-accessibility
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathscript.ts
More file actions
261 lines (217 loc) · 8.25 KB
/
script.ts
File metadata and controls
261 lines (217 loc) · 8.25 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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
interface quizData {
ask: string;
choose: string[];
answer: string;
}
//Quiz data
const quiz: quizData[] = [
{
ask: "When should you avoid using descriptive alt text for images?",
choose: ["For decorative images", "For informational images ", "For functional images"],
answer: "For decorative images"
},
{
ask: "What are the different levels of the Web content accessibility guidelines (WCAG)?",
choose: ["A, B, C ", "G, VG, MVG ", "A, AA, AAA"], answer: "A, AA, AAA"
},
{
ask: "Which description of flowers is easiest to understand?",
choose: ["Flowers symbolize emotions and culture.", "Flowers are nature's drip—bold and meaningful.", "Flowers are colorful plants that show feelings."],
answer: "Flowers symbolize emotions and culture."
}
]
//DOM Selectors
const quizSection = document.getElementById("quizSection") as HTMLSelectElement
const quizCard = document.getElementById("quizCard") as HTMLFieldSetElement
const quizQuestion = document.getElementById("quizQuestion") as HTMLHeadElement
const quizOptions = document.getElementById("quizOptions") as HTMLInputElement
const quizAnswer = document.getElementById("quizAnswer") as HTMLDivElement
const score = document.getElementById("score") as HTMLSpanElement
const restartBtn = document.getElementById("restartBtn") as HTMLButtonElement
const submitAnswer = document.getElementById("submitAnswer") as HTMLButtonElement
const quizFeedback = document.getElementById("quizFeedback") as HTMLDivElement
let index = 0, scr = 0;
let selectedOption: string | null = null;
let currentQuestion = 0;
let currentOption: string[] = [];
//let getQ = quiz, number,[] = [];
//instructions for screen readers at the start of the quiz
const quizInstructions = document.getElementById("quizInstructions");
//quizInstructions?.focus(); // Focus on instructions first
//Function to load the question
function loadQuestion(): void {
if (index >= quiz.length) return endQ(); // End if no more questions
const getQ = quiz[index];
currentQuestion = index; //Store current question, to go back to
currentOption = getQ.choose //Store current options, to go back to
if (quizQuestion) quizQuestion.textContent = getQ.ask;
if (quizOptions) quizOptions.innerHTML = ""; // Clear previous options
getQ.choose.forEach((element, i) => {
const btn = document.createElement("input");
btn.type = "radio";
btn.name = "option";
btn.id = `option-${i}`; // Unique ID for accessibility
btn.value = `${i + 1}. ${element}`;
btn.tabIndex = -1;
btn.setAttribute("aria-labelledby", `label-${i}`); //V Uses aria-labelledby for better screen reader support
btn.setAttribute("role", "radio"); //V
// V for the first radio button selected by deefault
if (i === 0) {
btn.checked = true;
btn.focus()
selectedOption = element;
}
// Allow arrow key navigation
btn.addEventListener("keydown", (event) => {
const radioButtons = document.querySelectorAll<HTMLInputElement>('input[name="option"]');
const currentIndex = Array.from(radioButtons).indexOf(btn);
if (event.key === "ArrowDown" || event.key === "ArrowRight") {
event.preventDefault();
const nextIndex = (currentIndex + 1) % radioButtons.length;
radioButtons[nextIndex].focus();
radioButtons[nextIndex].checked = true;
selectedOption = radioButtons[nextIndex].value;
} else if (event.key === "ArrowUp" || event.key === "ArrowLeft") {
event.preventDefault();
const prevIndex = (currentIndex - 1 + radioButtons.length) % radioButtons.length;
radioButtons[prevIndex].focus();
radioButtons[prevIndex].checked = true;
selectedOption = radioButtons[prevIndex].value;
}
});
btn.onclick = () => {
selectedOption = element;
};
const label = document.createElement("label");
label.htmlFor = btn.id;
label.id = `label-${i}`;
label.tabIndex = 0;
label.appendChild(btn);
label.append(` ${element}`)
label.addEventListener("keydown", function (event) {
if (event.key === "Enter") {
event.preventDefault();
btn.click(); // Ensure the button is clicked, not the label
}
});
// submitAnswer.onclick = (event) => {
// event.preventDefault(); //Stops form submission from refreshing the page
// if (selectedOption !== null) {
// checkA(selectedOption);
// }
// };
// Append elements
quizOptions?.appendChild(label);
});
trapFocus();
}
//Submit answer
submitAnswer.onclick = (event) => {
event.preventDefault(); //Stops form submission from refreshing the page
if (selectedOption !== null) {
checkA(selectedOption);
}
};
//Check if the answer is correct or not and show a feedback message
function checkA(opt: string): void {
//Remove quiz feedback
const quizFeedback = document.getElementById("quizFeedback") as HTMLDivElement | null;
if (quizFeedback) {
quizFeedback.remove();
}
if (opt === quiz[index].answer) {
scr++;
quizSection.insertAdjacentHTML(
"beforeend",
`<div id="quizFeedback" aria-live="polite">
<p tabindex="0">Correct answer!</p>
<button id="continueBtn">Continue to the next question</button>
</div>`);
} else if (opt !== quiz[index].answer) {
quizSection.insertAdjacentHTML(
"beforeend",
`<div id="quizFeedback" aria-live="polite">
<p tabindex="0">Oh no wrong answer, try again or continue to the next question!</p>
<button id="continueBtn">Continue to the next question</button>
</div>`);
}
//Click to continue to the next question
const continueBtn = document.getElementById("continueBtn") as HTMLButtonElement | null;
if (continueBtn) {
continueBtn.addEventListener("click", (event) => {
event.preventDefault();//Stops form submission from refreshing the page
//Remove quiz feedback
const quizFeedback = document.getElementById("quizFeedback") as HTMLDivElement | null;
if (quizFeedback) {
quizFeedback.remove();
}
index++;
loadQuestion()
})
}
}
//Funcion to get to the end of the quiz
function endQ(): void {
quizQuestion?.style.setProperty('display', 'none');
quizOptions?.style.setProperty('display', 'none');
quizAnswer?.style.setProperty('display', 'block');
if (score) score.textContent = scr.toString();
restartBtn?.style.setProperty('display', 'block');
submitAnswer.style.setProperty('display', 'none');
}
// V trap focus
function trapFocus() {
const focusableElements = quizCard.querySelectorAll<HTMLElement>(
'input, button'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
quizCard.addEventListener("keydown", (event) => {
if (event.key === "Tab") {
if (event.shiftKey) {
// Shift + Tab moves focus backward
if (document.activeElement === firstElement) {
event.preventDefault();
lastElement.focus();
}
} else {
// Tab moves focus forward
if (document.activeElement === lastElement) {
event.preventDefault();
firstElement.focus();
}
}
}
});
}
//Click to restart the quiz
restartBtn.addEventListener("click", () => {
index = 0;
scr = 0;
// Reset UI visibility
quizQuestion?.style.setProperty("display", "block");
quizOptions?.style.setProperty("display", "block");
quizAnswer?.style.setProperty("display", "none");
restartBtn?.style.setProperty("display", "none");
submitAnswer?.style.setProperty("display", "block");
loadQuestion()
})
function changeFont(event: Event): void {
const selectElement = event.target as HTMLSelectElement;
const selectedFont = selectElement.value;
// Reset all fonts to default
document.body.classList.remove('font1', 'font2');
// Apply the selected font
if (selectedFont === 'font1') {
document.body.classList.add('font1');
} else if (selectedFont === 'font2') {
document.body.classList.add('font2');
}
}
// Set up the font toggle
document.addEventListener('DOMContentLoaded', () => {
const fontToggle = document.getElementById('font-toggle') as HTMLSelectElement;
// Add event listener for font selection change
fontToggle.addEventListener('change', changeFont);
});
loadQuestion();