2
\$\begingroup\$

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>

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.