├── elements-helper.js ├── index.html ├── index.js ├── quiz-app-ss.jpg ├── quiz.js └── styles.css /elements-helper.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class QuizElementsHelper { 4 | /** 5 | * 6 | * @param app {Element} - the element of the whole app 7 | * @param quizCard {Element} - the wrapper of the quiz details 8 | * @param questionCard {Element} - the wrapper of the questions card 9 | * @param resultCard {Element} - the wrapper of the result card 10 | * @param quiz {Quiz} - an instance of the Quiz class 11 | */ 12 | constructor(app, quizCard, questionCard, resultCard, quiz) { 13 | this.app = app; 14 | this.quiz = quiz; 15 | this.quizCard = quizCard; 16 | this.questionCard = questionCard; 17 | this.resultCard = resultCard; 18 | 19 | // find & assign elements 20 | this.assignElements(); 21 | 22 | // initialize the listeners 23 | this.initListeners(); 24 | 25 | // show quiz details card 26 | this.showQuizCard(); 27 | } 28 | 29 | /** 30 | * find the inner elements of each card and assign to it. 31 | */ 32 | assignElements() { 33 | // Quiz Card Elements 34 | this.quizCard.startBtn = this.quizCard.querySelector( 35 | ".quiz-details__start-btn" 36 | ); 37 | this.quizCard.titleElm = this.quizCard.querySelector( 38 | ".quiz-details__title" 39 | ); 40 | this.quizCard.descriptionElm = this.quizCard.querySelector( 41 | ".quiz-details__description" 42 | ); 43 | this.quizCard.metaQCElm = this.quizCard.querySelector( 44 | ".quiz-details__meta.--qc strong" 45 | ); 46 | this.quizCard.metaTimeElm = this.quizCard.querySelector( 47 | ".quiz-details__meta.--t strong" 48 | ); 49 | 50 | // Question Card Elements 51 | this.questionCard.progressRemainingTimeElm = document.querySelector( 52 | ".questions-card__remaining-time" 53 | ); 54 | this.questionCard.progressQuestionCountElm = document.querySelector( 55 | ".questions-card__q-count" 56 | ); 57 | this.questionCard.progressbarElm = document.querySelector( 58 | ".questions-card__progress .--value" 59 | ); 60 | this.questionCard.questionTitleElm = document.getElementById( 61 | "question-title" 62 | ); 63 | this.questionCard.optionOneElm = document.querySelector( 64 | "#option-one ~ label" 65 | ); 66 | this.questionCard.optionTwoElm = document.querySelector( 67 | "#option-two ~ label" 68 | ); 69 | this.questionCard.optionThreeElm = document.querySelector( 70 | "#option-three ~ label" 71 | ); 72 | this.questionCard.optionFourElm = document.querySelector( 73 | "#option-four ~ label" 74 | ); 75 | this.questionCard.nextBtn = this.app.querySelector("#next-btn"); 76 | this.questionCard.stopBtn = this.app.querySelector("#stop-btn"); 77 | 78 | // Result Card Elements 79 | this.resultCard.gotoHome = this.resultCard.querySelector("#go-to-home"); 80 | this.resultCard.scoreElm = this.resultCard.querySelector("#score"); 81 | } 82 | 83 | /** 84 | * initialize the required listeners of the elements 85 | */ 86 | initListeners() { 87 | this.quizCard.startBtn.addEventListener( 88 | "click", 89 | this.showQuestionsCard.bind(this) 90 | ); 91 | this.questionCard.nextBtn.addEventListener( 92 | "click", 93 | this.nextBtnHandler.bind(this) 94 | ); 95 | this.questionCard.stopBtn.addEventListener( 96 | "click", 97 | this.stopBtnHandler.bind(this) 98 | ); 99 | this.resultCard.gotoHome.addEventListener( 100 | "click", 101 | this.hideResultCard.bind(this) 102 | ); 103 | } 104 | 105 | /** 106 | * Show the details card of the quiz 107 | */ 108 | showQuizCard() { 109 | this.quizCard.titleElm.innerText = this.quiz.title; 110 | this.quizCard.descriptionElm.innerText = this.quiz.description; 111 | this.quizCard.metaQCElm.innerText = this.quiz._questions.length; 112 | this.quizCard.metaTimeElm.innerText = this.quiz._time; 113 | 114 | this.quizCard.classList.add("show"); 115 | } 116 | 117 | /** 118 | * hide the quiz card 119 | */ 120 | hideQuizCard() { 121 | this.quizCard.classList.remove("show"); 122 | } 123 | 124 | /** 125 | * Show the question card 126 | */ 127 | showQuestionsCard() { 128 | this.hideQuizCard(); 129 | 130 | this.questionCard.classList.add("show"); 131 | this.questionCard.classList.remove("time-over"); 132 | 133 | this.startQuiz(); 134 | } 135 | 136 | /** 137 | * hide the question card 138 | */ 139 | hideQuestionsCard() { 140 | this.questionCard.classList.remove("show"); 141 | } 142 | 143 | /** 144 | * Handle the visibility of the result card 145 | * @param result - the object of quiz result thet contains score property 146 | */ 147 | showResultCard(result) { 148 | this.hideQuestionsCard(); 149 | 150 | if (this.resultCard.scoreElm && result) 151 | this.resultCard.scoreElm.innerText = Math.floor(result.score * 10) / 10; 152 | 153 | this.resultCard.classList.add("show"); 154 | } 155 | 156 | /** 157 | * hide the result card 158 | */ 159 | hideResultCard() { 160 | this.resultCard.classList.remove("show"); 161 | this.showQuizCard(); 162 | } 163 | 164 | /** 165 | * Handle the starting of the quiz and control the status of it. 166 | */ 167 | startQuiz() { 168 | this.resetPreviousQuiz(); 169 | this.quiz.reset(); 170 | const firstQuestion = this.quiz.start(); 171 | if (firstQuestion) { 172 | this.parseNextQuestion(firstQuestion); 173 | } 174 | 175 | this.questionCard.nextBtn.innerText = "Next"; 176 | 177 | this._setProgressTicker(); 178 | } 179 | 180 | /** 181 | * initialize the quiz time progress on every time that quiz starts 182 | * to control the progressbar and remaining time 183 | * @private 184 | */ 185 | _setProgressTicker() { 186 | this.remainingTimeInterval = setInterval(() => { 187 | const qTime = this.quiz.timeDetails; 188 | if (qTime && qTime.remainingTime) { 189 | // update remaining time span 190 | this.questionCard.progressRemainingTimeElm.innerText = 191 | qTime.remainingTime; 192 | 193 | // update progressbar 194 | let progressPercent = 195 | ((qTime.quizTime - qTime.elapsedTime) * 100) / qTime.quizTime; 196 | if (progressPercent < 0) progressPercent = 0; 197 | this.questionCard.progressbarElm.style.width = progressPercent + "%"; 198 | } 199 | 200 | // clear & stop interval when time over 201 | if (qTime.timeOver) { 202 | this.questionCard.classList.add("time-over"); 203 | this.questionCard.nextBtn.innerText = "Show Result"; 204 | clearInterval(this.remainingTimeInterval); 205 | } 206 | }, 1000); 207 | } 208 | 209 | /** 210 | * this method putting the question in the question card 211 | * @param question - the object of the question that received from this.quiz 212 | */ 213 | parseNextQuestion(question) { 214 | const selectedOption = document.querySelector( 215 | "input[name=question-option]:checked" 216 | ); 217 | 218 | this.questionCard.progressQuestionCountElm.innerText = `Question ${this.quiz 219 | ._currentQuestionIndex + 1}/${this.quiz._questions.length}`; 220 | this.questionCard.questionTitleElm.setAttribute( 221 | "data-qn", 222 | `Q ${this.quiz._currentQuestionIndex + 1}:` 223 | ); 224 | this.questionCard.questionTitleElm.innerText = question.title; 225 | 226 | this.questionCard.optionOneElm.innerText = question.options[0]; 227 | this.questionCard.optionTwoElm.innerText = question.options[1]; 228 | this.questionCard.optionThreeElm.innerText = question.options[2]; 229 | this.questionCard.optionFourElm.innerText = question.options[3]; 230 | 231 | // reset pre selected options on every next 232 | if (selectedOption) selectedOption.checked = false; 233 | } 234 | 235 | /** 236 | * To reset the previous quiz status before restarting it. 237 | */ 238 | resetPreviousQuiz() { 239 | this.quiz.stop(); 240 | clearInterval(this.remainingTimeInterval); 241 | 242 | this.resultCard.scoreElm.innerText = 0; 243 | this.questionCard.progressRemainingTimeElm.innerText = "00:00"; 244 | this.questionCard.progressbarElm.style.width = "100%"; 245 | } 246 | 247 | /** 248 | * this will call when next button clicked 249 | */ 250 | nextBtnHandler() { 251 | const selectedOption = document.querySelector( 252 | "input[name=question-option]:checked" 253 | ); 254 | 255 | let result; 256 | if (!selectedOption) { 257 | result = this.quiz.skipCurrentQuestion(); 258 | } else { 259 | result = this.quiz.answerCurrentQuestion(selectedOption.value); 260 | } 261 | 262 | if (result.finished || result.timeOver) { 263 | this.showResultCard(result.result); 264 | } else if (result) { 265 | this.parseNextQuestion(result.nextQ); 266 | } 267 | } 268 | 269 | /** 270 | * this will call when stop button clicked 271 | */ 272 | stopBtnHandler() { 273 | this.resetPreviousQuiz(); 274 | this.showResultCard(); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 |