We’ve built a custom quiz app that uses Webflow’s Native slider & Finsweet CMS Slider solution.
What we do is -
-
We’ve written a custom script to handle quiz functions – user option select, Validate if answer right / wrong, enable next button based on that.
-
Calculate progress as user completes each question
-
When user clicks next button, we navigate to the next slide.
-
Once last question is answered & progress is 100, next button should not be clicked and a success state is shown where user completes the quiz.
The issue we are facing -
-
As an edge case, one some PC browsers, when user answers last question, they are kicked back to first slide. We debugged this to be happening as a result of the next button clicked before progress turns 100. And added a callback to make sure the button click happens only after progress is calculated. But still this issue seems to happen on one computer.
-
One some PC browsers, the center aligned slide content gets misaligned. I think because the PC is too old or outdated its painting the content wrongly? And as a result the kicking back to first slide happend?
Here is a loom video of the issue -
Here is the script -
const handleQuiz = () => {
let selectedOptionWrapper, isCorrect, progress;
const quizModal = new WFComponent(`[xa-type="quiz-modal"]`);
const quizQuestionItems = quizModal.getChildAsComponents(`.quiz_question-item`);
const quizOptionWrappers = quizModal.getChildAsComponents(`.quiz_option-wrapper`);
const quizOptionCircles = quizModal.getChildAsComponents(`.quiz_option-circle`);
const quizExplanations = quizModal.getChildAsComponents(`.quiz_question-explanation`);
const quizAppreciations = quizModal.getChildAsComponents(`.quiz_question-correct`);
const quizNextBtn = quizModal.getChildAsComponent(`#quiz-next`);
const quizClose = quizModal.getChildAsComponent(`#quiz-modal-close`);
const quizProgressBar = quizModal.getChildAsComponent(`.quiz_progress-bar`);
const quizSlider = new WFSlider(`[xa-type="quiz-slider"]`);
const allQuestionSlides = quizSlider.getAllSlides();
const quizNextLesson = quizModal.getChildAsComponent(`#quiz-next-lesson`);
// const quizCompleteCourse = quizModal.getChildAsComponent(`#quiz-complete-course`);
const quizSuccess = quizModal.getChildAsComponent(`.quiz-modal_success-wrap`);
const quizBody = quizModal.getChildAsComponent(`.quiz-modal_body-wrap`);
const identifyNextLesson = quizModal.getChildAsComponent(`.identify-next-lesson`);
const quizFooter = quizModal.getChildAsComponent(`.quiz-modal_footer`);
quizOptionWrappers.forEach((optionWrapper) => {
optionWrapper.on("click", () => {
const optionCircle = optionWrapper.getChildAsComponent(`.quiz_option-circle`);
quizOptionWrappers.forEach(ow => { ow.getElement().classList.remove("selected", "correct", "wrong"); });
quizOptionCircles.forEach(oc => { oc.getElement().classList.remove("selected", "correct", "wrong"); });
quizExplanations.forEach(qe => { qe.getElement().classList.remove("show"); });
quizAppreciations.forEach(qa => { qa.getElement().classList.remove("show"); });
optionWrapper.addCssClass("selected");
optionCircle.addCssClass("selected");
quizNextBtn.removeCssClass("is-disabled");
quizNextBtn.removeAttribute("disabled");
});
});
quizClose.on("click", () => {
progress = 0;
quizProgressBar.setStyle({ width: "1%" });
quizQuestionItems.forEach(qi => { qi.getElement().classList.remove("answered"); });
quizOptionWrappers.forEach(ow => { ow.getElement().classList.remove("selected", "correct", "wrong"); });
quizOptionCircles.forEach(oc => { oc.getElement().classList.remove("selected", "correct", "wrong"); });
quizExplanations.forEach(qe => { qe.getElement().classList.remove("show"); });
quizAppreciations.forEach(qa => { qa.getElement().classList.remove("show"); });
quizSlider.goToIndex(0);
});
quizNextBtn.on("click", () => {
quizNextBtn.addCssClass("is-disabled");
quizNextBtn.setAttribute("disabled", "true");
selectedOptionWrapper = quizModal.getChildAsComponent(`.quiz_option-wrapper.selected`);
const selectedOptionCircle = selectedOptionWrapper.getChildAsComponent(`.quiz_option-circle`);
const currentQuestion = selectedOptionWrapper.getElement().closest(`.quiz_question-item`);
const quizCorrectAlert = currentQuestion.querySelector(`.quiz_question-correct`);
const quizWrongAlert = currentQuestion.querySelector(`.quiz_question-explanation`);
isCorrect = selectedOptionWrapper.getElement().querySelector('.correct') !== null;
selectedOptionWrapper.removeCssClass("selected");
selectedOptionCircle.removeCssClass("selected");
if (isCorrect) {
selectedOptionWrapper.addCssClass("correct");
selectedOptionCircle.addCssClass("correct");
currentQuestion.classList.add("answered");
quizCorrectAlert.classList.add("show");
updateProgress(() => {
console.log(progress);
setTimeout(() => {
if (progress === 100) {
completeQuiz();
} else {
navToNextQuestion();
}
}, 1050);
});
} else {
selectedOptionWrapper.addCssClass("wrong");
selectedOptionCircle.addCssClass("wrong");
quizWrongAlert.classList.add("show");
quizNextBtn.addCssClass('is-disabled');
quizNextBtn.setAttribute("disabled", "true");
}
});
const updateProgress = (cb: () => void) => {
const answeredQuestions = quizModal.getChildAsComponents(`.answered`)
progress = (answeredQuestions.length / quizQuestionItems.length) * 100;
quizProgressBar.setStyle({ width: `${progress}%` });
cb();
}
const completeQuiz = () => {
quizNextBtn.addCssClass('hide'); quizNextBtn.setAttribute("disabled", "true");
quizClose.addCssClass("hide");
quizNextLesson.removeCssClass("hide");
// quizCompleteCourse.removeCssClass("hide");
quizSuccess.addCssClass("show");
quizBody.addCssClass("hide");
if (identifyNextLesson.getTextContent().trim() === '') {
quizFooter.addCssClass("hide");
}
console.log("Quiz Completed");
}
const navToNextQuestion = () => {
if (quizSlider.getActiveSlideIndex() !== allQuestionSlides.length) {
requestAnimationFrame(() => {
quizSlider.goNext();
})
console.log("Went to next question");
}
}
}