Welcome to the Frontend Quiz!
47 |Pick a subject to get started.
48 |Question of
85 |Placeholder
86 |Quiz completed 122 | You scored... 123 |
124 |some Subject
134 |out of totalQ
137 |├── .gitignore ├── README.md ├── Requirements.md ├── app.js ├── assets ├── fonts │ ├── OFL.txt │ ├── README.txt │ ├── Rubik-Italic-VariableFont_wght.ttf │ ├── Rubik-VariableFont_wght.ttf │ └── static │ │ ├── Rubik-Italic.ttf │ │ ├── Rubik-Medium.ttf │ │ └── Rubik-Regular.ttf └── images │ ├── favicon-32x32.png │ ├── icon-accessibility.svg │ ├── icon-correct.svg │ ├── icon-css.svg │ ├── icon-error.svg │ ├── icon-html.svg │ ├── icon-incorrect.svg │ ├── icon-js.svg │ ├── icon-moon-dark.svg │ ├── icon-moon-light.svg │ ├── icon-sun-dark.svg │ ├── icon-sun-light.svg │ ├── pattern-background-desktop-dark.svg │ ├── pattern-background-desktop-light.svg │ ├── pattern-background-mobile-dark.svg │ ├── pattern-background-mobile-light.svg │ ├── pattern-background-tablet-dark.svg │ └── pattern-background-tablet-light.svg ├── data.json ├── index.html ├── preview.jpg ├── screenshots ├── desktop-dark.png ├── desktop-light.png ├── mobile-dark.png └── mobile-light.png └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | # Avoid accidental upload of the Sketch and Figma design files 2 | ##################################################### 3 | ## Please do not remove lines 5 and 6 - thanks! 🙂 ## 4 | ##################################################### 5 | *.sketch 6 | *.fig 7 | 8 | # Avoid accidental XD upload if you convert the design file 9 | ############################################### 10 | ## Please do not remove line 12 - thanks! 🙂 ## 11 | ############################################### 12 | *.xd 13 | 14 | # Avoid your project being littered with annoying .DS_Store files! 15 | .DS_Store 16 | .prettierignore -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frontend Mentor - Frontend quiz app solution 2 | 3 | This is a solution to the [Frontend quiz app challenge on Frontend Mentor](https://www.frontendmentor.io/challenges/frontend-quiz-app-BE7xkzXQnU). Frontend Mentor challenges help you improve your coding skills by building realistic projects. 4 | 5 | ## Table of contents 6 | 7 | - [Overview](#overview) 8 | - [The challenge](#the-challenge) 9 | - [Screenshot](#screenshot) 10 | - [Links](#links) 11 | - [My process](#my-process) 12 | - [Built with](#built-with) 13 | - [What I learned](#what-i-learned) 14 | - [Continued development](#continued-development) 15 | - [Useful resources](#useful-resources) 16 | - [Author](#author) 17 | 18 | ## Overview 19 | 20 | ### The challenge 21 | 22 | Users should be able to: 23 | 24 | - Select a quiz subject 25 | - Select a single answer from each question from a choice of four 26 | - See an error message when trying to submit an answer without making a selection 27 | - See if they have made a correct or incorrect choice when they submit an answer 28 | - Move on to the next question after seeing the question result 29 | - See a completed state with the score after the final question 30 | - Play again to choose another subject 31 | - View the optimal layout for the interface depending on their device's screen size 32 | - See hover and focus states for all interactive elements on the page 33 | - Navigate the entire app only using their keyboard 34 | - **Bonus**: Change the app's theme between light and dark 35 | 36 | ### Screenshot 37 | 38 |  39 |  40 |  41 |  42 | 43 | ### Links 44 | 45 | - Solution URL: [Solution URL](https://github.com/rainSaxFrontend-Mentor-Projects/frontend-quiz-app) 46 | - Live Site URL: [Live site URL](https://rainsaxfrontend-mentor-projects.github.io/frontend-quiz-app/) 47 | 48 | ## My process 49 | 50 | ### Built with 51 | 52 | - Semantic HTML5 markup 53 | - CSS custom properties 54 | - Flexbox 55 | - CSS Grid 56 | - Mobile-first workflow 57 | 58 | ### What I learned 59 | 60 | In doing this project I got a ton of practice with javascript and CSS. One of the biggest new things I learned was how to retrive data from a .json file. This involved learning and working with fetch(), Promises, await, and .json(), all of which were new to me. Another new approach I got more comfortable with was the practice of creating a class in CSS such as .visible, and having js simply toggle that class on and off when appropriate. This was my refactored approach to each of the quiz screens and through the CSS I was able to delegate .visible's activities via media queries. 61 | 62 | ```js 63 | async function getQuiz(type) { 64 | const response = await fetch('./data.json'); 65 | const data = await response.json(); 66 | for (const quiz of data.quizzes) { 67 | if (quiz.title == type) { 68 | quizChosen = quiz; 69 | totalQuestions = quizChosen.questions.length; 70 | document.querySelector(".question-total").textContent = totalQuestions 71 | increment = (1 / totalQuestions) * 100; 72 | } 73 | } 74 | makeQuestions(quizChosen) 75 | } 76 | ``` 77 | 78 | ### Continued development 79 | 80 | I think it will be fun to continue working on this assignment as an on going project. For the future, I plan to add difficulty levels with different sets of questions. The program already allows for a variable number of questions. 81 | 82 | ### Useful resources 83 | 84 | - [fetch with json](https://dmitripavlutin.com/fetch-with-json/) - This helped me understand how to use await, fetch, and .json() 85 | 86 | ## Author 87 | 88 | - Frontend Mentor - [@rainSax](https://www.frontendmentor.io/profile/rainSax) 89 | -------------------------------------------------------------------------------- /Requirements.md: -------------------------------------------------------------------------------- 1 | -Light / Dark toggle, need to research this - DONE 2 | - reading data from json file 3 | - organize content in a grid -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // light dark switch section 2 | const toggleSwitch = document.querySelector('.light-dark-switch input[type="checkbox"]'); 3 | document.querySelector(".start-menu").classList.toggle("visible") 4 | 5 | function switchMode(event) { 6 | if (event.target.checked) { 7 | document.documentElement.setAttribute('data-theme', 'dark'); 8 | } 9 | else { 10 | document.documentElement.setAttribute('data-theme', 'light'); 11 | } 12 | } 13 | 14 | toggleSwitch.addEventListener('change', switchMode, false); 15 | 16 | var quizButtons = document.querySelectorAll(".quiz-type"); 17 | var quizType; 18 | 19 | for (var i = 0; i < quizButtons.length; i++) { 20 | quizButtons[i].addEventListener("click", function () { 21 | quizType = this.id; 22 | questionScreen(quizType); 23 | }) 24 | } 25 | 26 | function questionScreen(type) { 27 | document.querySelector(".start-menu").classList.toggle("visible") 28 | setSubjectBars(type) 29 | document.querySelector(".question-screen").classList.toggle("visible") 30 | 31 | //retrieve quiz data based on selection 32 | getQuiz(type); 33 | } 34 | 35 | function setSubjectBars(type) { 36 | var bars = document.querySelectorAll(".curr-subject"); 37 | for (let bar of bars) { 38 | bar.lastElementChild.innerHTML = type 39 | if (type == "HTML") { 40 | bar.firstElementChild.firstElementChild.src = "./assets/images/icon-html.svg" 41 | } 42 | else if (type == "CSS") { 43 | bar.firstElementChild.firstElementChild.src = "./assets/images/icon-css.svg" 44 | } 45 | else if (type == "JavaScript") { 46 | bar.firstElementChild.firstElementChild.src = "./assets/images/icon-js.svg" 47 | } 48 | else { 49 | bar.firstElementChild.firstElementChild.src = "./assets/images/icon-accessibility.svg" 50 | } 51 | bar.style.visibility = "visible" 52 | } 53 | } 54 | 55 | var quizChosen; 56 | var qCount = -1; 57 | var totalQuestions; 58 | var score = 0; 59 | var submit = document.querySelector(".submit-answer"); 60 | var increment; 61 | 62 | // fetch returns a Promise, .json() returns a *2nd* Promise, therefore 2 .thens 63 | async function getQuiz(type) { 64 | const response = await fetch('./data.json'); 65 | const data = await response.json(); 66 | for (const quiz of data.quizzes) { 67 | if (quiz.title == type) { 68 | quizChosen = quiz; 69 | totalQuestions = quizChosen.questions.length; 70 | document.querySelector(".question-total").textContent = totalQuestions 71 | increment = (1 / totalQuestions) * 100; 72 | } 73 | } 74 | makeQuestions(quizChosen) 75 | } 76 | 77 | // quiz flow: 78 | // populate fields -> submit event handler validates (wrong - show wrong, do nothing. right - show right, move on) 79 | 80 | function makeQuestions(quizChoice) { 81 | qCount++; 82 | document.querySelector(".question-number").textContent = (qCount + 1); 83 | document.querySelector(".progress-bar.done").style.width = (increment * (qCount + 1)).toString() + "%"; 84 | submit.textContent = "Submit" 85 | let options = document.querySelectorAll(".option"); 86 | 87 | document.querySelector(".question").textContent = quizChoice.questions[qCount].question; 88 | 89 | for (let option of options) { 90 | option.classList.remove("selected") 91 | option.classList.remove("invalid") 92 | option.classList.remove("correct") 93 | } 94 | 95 | for (let i = 0; i < options.length; i++) { 96 | switch (i) { 97 | case 0: options[i].innerHTML = "
elements?",
155 | "options": [
156 | "p { }",
157 | ".p { }",
158 | "#p { }",
159 | "all.p { }"
160 | ],
161 | "answer": "p { }"
162 | },
163 | {
164 | "question": "Which property is used to change the font of an element?",
165 | "options": [
166 | "font-style",
167 | "text-style",
168 | "font-family",
169 | "typeface"
170 | ],
171 | "answer": "font-family"
172 | },
173 | {
174 | "question": "How do you make each word in a text start with a capital letter?",
175 | "options": [
176 | "text-transform: capitalize",
177 | "text-transform: uppercase",
178 | "text-style: capital",
179 | "font-transform: capitalize"
180 | ],
181 | "answer": "text-transform: capitalize"
182 | },
183 | {
184 | "question": "How do you select an element with the class name 'header'?",
185 | "options": [
186 | ".header",
187 | "#header",
188 | "header",
189 | "*header"
190 | ],
191 | "answer": ".header"
192 | },
193 | {
194 | "question": "What is the default value of the 'position' property?",
195 | "options": [
196 | "relative",
197 | "fixed",
198 | "absolute",
199 | "static"
200 | ],
201 | "answer": "static"
202 | },
203 | {
204 | "question": "What is the purpose of the z-index property in CSS?",
205 | "options": [
206 | "To count the number of elements",
207 | "To set the magnification level of an element",
208 | "To specify the stack order of an element",
209 | "To create a zoom effect"
210 | ],
211 | "answer": "To specify the stack order of an element"
212 | }
213 | ]
214 | },
215 | {
216 | "title": "JavaScript",
217 | "icon": "./assets/images/icon-js.svg",
218 | "questions": [
219 | {
220 | "question": "Which syntax is correct to output 'Hello World' in an alert box?",
221 | "options": [
222 | "alertBox('Hello World');",
223 | "msg('Hello World');",
224 | "alert('Hello World');",
225 | "msgBox('Hello World');"
226 | ],
227 | "answer": "alert('Hello World');"
228 | },
229 | {
230 | "question": "How do you call a function named 'myFunction'?",
231 | "options": [
232 | "call function myFunction()",
233 | "call myFunction()",
234 | "myFunction()",
235 | "execute myFunction()"
236 | ],
237 | "answer": "myFunction()"
238 | },
239 | {
240 | "question": "How to write an IF statement in JavaScript?",
241 | "options": [
242 | "if i = 5 then",
243 | "if (i == 5)",
244 | "if i == 5",
245 | "if i = 5"
246 | ],
247 | "answer": "if (i == 5)"
248 | },
249 | {
250 | "question": "How to write an IF statement for executing some code if 'i' is NOT equal to 5?",
251 | "options": [
252 | "if (i <> 5)",
253 | "if i =! 5 then",
254 | "if (i != 5)",
255 | "if i not = 5"
256 | ],
257 | "answer": "if (i != 5)"
258 | },
259 | {
260 | "question": "How does a FOR loop start?",
261 | "options": [
262 | "for (i = 0; i <= 5)",
263 | "for i = 1 to 5",
264 | "for (i <= 5; i++)",
265 | "for (i = 0; i <= 5; i++)"
266 | ],
267 | "answer": "for (i = 0; i <= 5; i++)"
268 | },
269 | {
270 | "question": "How can you add a comment in a JavaScript?",
271 | "options": [
272 | "'This is a comment",
273 | "//This is a comment",
274 | "",
275 | "/* This is a comment */"
276 | ],
277 | "answer": "//This is a comment"
278 | },
279 | {
280 | "question": "What is the correct way to write a JavaScript array?",
281 | "options": [
282 | "var colors = (1:'red', 2:'green', 3:'blue')",
283 | "var colors = ['red', 'green', 'blue']",
284 | "var colors = 'red', 'green', 'blue'",
285 | "var colors = 1 = ('red'), 2 = ('green'), 3 = ('blue')"
286 | ],
287 | "answer": "var colors = ['red', 'green', 'blue']"
288 | },
289 | {
290 | "question": "How do you find the number with the highest value of x and y?",
291 | "options": [
292 | "Math.ceil(x, y)",
293 | "top(x, y)",
294 | "Math.max(x, y)",
295 | "Math.highest(x, y)"
296 | ],
297 | "answer": "Math.max(x, y)"
298 | },
299 | {
300 | "question": "Which operator is used to assign a value to a variable?",
301 | "options": [
302 | "-",
303 | "*",
304 | "=",
305 | "x"
306 | ],
307 | "answer": "="
308 | },
309 | {
310 | "question": "What is the correct way to write a JavaScript object?",
311 | "options": [
312 | "var person = {firstName: 'John', lastName: 'Doe'};",
313 | "var person = {firstName = 'John', lastName = 'Doe'};",
314 | "var person = (firstName: 'John', lastName: 'Doe');",
315 | "var person = (firstName = 'John', lastName = 'Doe');"
316 | ],
317 | "answer": "var person = {firstName: 'John', lastName: 'Doe'};"
318 | }
319 | ]
320 | },
321 | {
322 | "title": "Accessibility",
323 | "icon": "./assets/images/icon-accessibility.svg",
324 | "questions": [
325 | {
326 | "question": "What does 'WCAG' stand for?",
327 | "options": [
328 | "Web Content Accessibility Guidelines",
329 | "Web Compliance Accessibility Guide",
330 | "Web Content Accessibility Goals",
331 | "Website Compliance and Accessibility Guidelines"
332 | ],
333 | "answer": "Web Content Accessibility Guidelines"
334 | },
335 | {
336 | "question": "Which element is used to provide alternative text for images for screen reader users?",
337 | "options": [
338 | ""
342 | ],
343 | "answer": "
"
344 | },
345 | {
346 | "question": "What does ARIA stand for in web development?",
347 | "options": [
348 | "Accessible Rich Internet Applications",
349 | "Advanced Responsive Internet Assistance",
350 | "Accessible Responsive Internet Applications",
351 | "Automated Responsive Internet Actions"
352 | ],
353 | "answer": "Accessible Rich Internet Applications"
354 | },
355 | {
356 | "question": "Which of the following is not a principle of the WCAG?",
357 | "options": [
358 | "Perceivable",
359 | "Dependable",
360 | "Operable",
361 | "Understandable"
362 | ],
363 | "answer": "Dependable"
364 | },
365 | {
366 | "question": "Which of these color contrast ratios defines the minimum WCAG 2.1 Level AA requirement for normal text?",
367 | "options": [
368 | "3:1",
369 | "4.5:1",
370 | "7:1",
371 | "2:1"
372 | ],
373 | "answer": "4.5:1"
374 | },
375 | {
376 | "question": "Which of the following elements is inherently focusable, meaning it can receive focus without a 'tabindex' attribute?",
377 | "options": [
378 | "