I wrote the below answer for the following question on SO a couple of days ago, and I was curious if the usage of the generator function is an acceptable one.
I choose the generator function, because I could easily exchange the generator at a later time to ask the question not in sequence, but rather randomized, or whatever algorithm I might come up with at that time :)
One thing I am not entirely fond of is the restart functionality. I guess it might be better to have the logic for restarting the questionaire rather implemented inside the onCompleted
callback I have now, rather than as part of the "official" end of the questionaire.
Because of this, I am now reusing an already called callback, so I guess these are things that can be improved.
Still I am curious of having a review of the usage of the generator here, and the potential overlooked drawbacks (the fact that only next is available is not something that I find a problem, I wanted it unidirectional)
const questions = [
{
question: 'What platform are you on?',
answers: ['Stackoverflow', 'codereview'],
correct: 1,
points: 5
},
{
question: 'What is the answer to everything',
answers: [42, 'I don\'t have a clue'],
correct: 0,
points: 1
},
{
question: 'How much is 7*6',
answers: ['I am not good with maths', 42],
correct: 1,
points: 10
}
];
// a simple generator that is used in the questionaire
function *questionsGenerator( questions ) {
yield* questions;
}
const questionaire = ( query, nextButton, target, onCompleted ) => {
let score = 0;
let totalScore = 0;
let iterator = questionsGenerator( query );
let question = null;
let selectedAnswer = -1;
nextButton.addEventListener('click', nextQuestion);
function evaluateAnswer() {
if (!question) {
// no question yet
return;
}
if (selectedAnswer < 0) {
return;
}
totalScore += question.points;
if (question.correct === selectedAnswer) {
score += question.points;
}
return;
}
function restart() {
btnNext.removeEventListener('click', restart);
questionaire( query, nextButton, target, onCompleted );
btnNext.innerHTML = 'next';
}
function nextQuestion() {
evaluateAnswer();
question = iterator.next();
// this is a bit of a hack to check if we just had the last question or not
if (question.done) {
nextButton.removeEventListener('click', nextQuestion);
nextButton.innerText = 'Restart';
btnNext.addEventListener('click', restart);
onCompleted( (score / totalScore) * 100 );
return;
}
question = question.value;
drawUI();
}
function drawUI() {
// disable next button
nextButton.setAttribute('disabled', true);
selectedAnswer = -1;
// remove existing items
Array.from( target.childNodes ).forEach( child => target.removeChild( child ) );
// create new questions (if available)
const title = document.createElement('h1');
title.innerHTML = question.question;
target.appendChild( title );
question.answers.map( (answer, i) => {
const el = document.createElement('input');
el.type = 'radio';
el.name = 'answer';
el.value = i;
el.id = 'answer' + i;
el.addEventListener('change', () => {
selectedAnswer = i;
nextButton.removeAttribute('disabled');
} );
const label = document.createElement('label');
label.setAttribute('for', el.id );
label.innerHTML = answer;
const container = document.createElement('div');
container.appendChild(el);
container.appendChild(label);
return container;
} ).forEach( a => target.appendChild( a ) );
}
nextQuestion();
};
// create a questionaire and start the first question
questionaire(
questions,
document.querySelector('#btnNext'),
document.querySelector('#questionaire'),
score => alert('You scored ' + score )
);
<div id="questionaire">
</div>
<div class="toolstrip">
<button id="btnNext" type="button">Next</button>
</div>