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\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.