0

I am trying to create a questionnaire but instead of using "radio buttons" I would like the user to be able to write the answer to each of the questions in a "text box" but I do not see how to do it and have many errors. Could you please help me ?

const questions = [
  { 
    question: 'What platform are you on?',
    answers: 'Stackoverflow',
    points: 5
  },
  { 
    question: '40 + 2?',
    answers: 42,
    points: 1
  },
  { 
    question: 'How much is 7*6',
    answers: 42,
    points: 1
  }
];

// a simple generator that is used in the questionaire
function *questionsGenerator( questions ) {
  yield* questions;
}

// creates a questionaire, with forward only options (due to the use of the generator function)
// all variables are locally scoped, when all questions were answered, the onCompleted callback would be called
// it returns an object with nextQuestion function though it could call nextButton internally, and you just have to call the function once if you would want to change it
const questionaire = ( query, nextButton, target, onCompleted ) => {
  let score = 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;
    }
    if (question.correct === selectedAnswer) {
      score += question.points;
    }
    return;
  }
  
  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);
      onCompleted( score );
      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 = 'text';
      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 ) );
  }
  
  return {
    nextQuestion
  }
};

// create a questionaire and start the first question
questionaire(
  questions, 
  document.querySelector('input'[id="text"]).value,
  document.querySelector('#btnNext'), 
  document.querySelector('#questionaire'), 
  score => alert('You scored ' + score ) 
).nextQuestion();
<h1>questionnaire</h1>
<div id="questionaire"></div>
<input type="text" id="text"/>
<div class="toolstrip">
  <button id="btnNext" type="button">Next</button>
</div>
Zoe
  • 27,060
  • 21
  • 118
  • 148

1 Answers1

1

I don't know exactly how you want it to work, but I created a minimal-change version that at least doesn't have any bugs so you can work with it:

<h1>questionnaire</h1>
<div id="questionaire"></div>
<div class="toolstrip">
  <button id="btnNext" type="button">Next</button>
</div>
const questions = [
  { 
    question: 'What platform are you on?',
    answers: 'Stackoverflow',
    points: 5
  },
  { 
    question: '40 + 2?',
    answers: "42",
    points: 1
  },
  { 
    question: 'How much is 7*6',
    answers: "42",
    points: 1
  }
];

// a simple generator that is used in the questionaire
function *questionsGenerator( questions ) {
  yield* questions;
}

// creates a questionaire, with forward only options (due to the use of the generator function)
// all variables are locally scoped, when all questions were answered, the onCompleted callback would be called
// it returns an object with nextQuestion function though it could call nextButton internally, and you just have to call the function once if you would want to change it
const questionaire = ( query, nextButton, target, onCompleted ) => {
  let score = 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;
    }
    if (question.answers === selectedAnswer) {
      score += question.points;
    }
    return;
  }
  
  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);
      onCompleted( score );
      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 = 'text';
      el.name = 'answer';
      el.value = '';
      el.id = 'answer';
      el.addEventListener('change', () => { 
        selectedAnswer = el.value.trim(); 
     
      } );
      el.addEventListener('keydown', () => { 
        nextButton.removeAttribute('disabled'); 
      } );
            
      const container = document.createElement('div');
      container.appendChild(el);
      
      //return container;      
    //} ).forEach( a => target.appendChild( a ) );
      target.appendChild(container);
      el.focus();
  }
  
  return {
    nextQuestion
  }
};

// create a questionaire and start the first question
questionaire(
  questions, 
//  document.querySelector('input[id="text"]'),
  document.querySelector('#btnNext'), 
  document.querySelector('#questionaire'), 
  score => alert('You scored ' + score ) 
).nextQuestion();

as per this fiddle https://jsfiddle.net/sbv9o12d/

Make sure all answers are strings, because numeric answers aren't parsed.

kikon
  • 3,670
  • 3
  • 5
  • 20