Sasank Ganapathiraju
Sasank Ganapathiraju

Reputation: 135

Jquery/Javascript: Countdown Timer acting erratically

I am trying to make a simple quiz using javascript with timed questions. Each question lasts for 10 seconds before moving on to the next. The countdown timer does well for the first question, after which it starts to speed up or display random numbers for following questions. Here is my code, P.S. I apologize for the inefficient and hectic code, I'm still new to javascript and I will make the code more streamlined once I fix this issue.

var questionList = [
        {
            q:"What is a dog?",
            a:["fish","mammal","plant","prokaryote"],
            answer: 1
        },
         {
            q:"What is a cat?",
            a:["mammal","fish","plant","amphibian"],
             answer: 0
        },
         {
            q:"What is a tree?",
            a:["plant","fish","mammal","none"],
             answer: 0
        },
         {
            q:"What do cars run on?",
            a:["gasoline","water","ethanol","liquid oxygen"],
             answer: 0
        },
        {
            q:"What is 4 x 4?",
            a:["8","16","4","160"],
            answer: 1
        },
        {
            q:"What is the capital of Australia?",
            a:["Brisbane","GoldCoast","Perth","Canberra","Melbourne"],
            answer: 3
        },
        {
            q:"What is the national flower of Canada?",
            a:["sunflower","daisy","trillium","rose","lotus"],
            answer: 2
        }
    ];
    //--------------------------------------
    var picked;
    var qcount = 0;
    var output = [];
    var timer;
    var timer2;
    var timeLeft = 10;
    var correctQ = 0;
    var wrongQ = 0;
    //var randomQ = Math.floor(Math.random()*7);
    var x = questionList[qcount];
    var j = x.answer;
   // var cAns = x.a[j];
    //console.log(cAns);
    console.log(j);
    //new Q w/ options
    function qGen(){
        timer = setInterval(time, 1000)
        $('#question').text(x.q);
        for (var i=0; i < (x.a).length; i++){
            var newLi = $('<button>');
            newLi.attr('data-id', i);
            newLi.addClass("answer").text(x.a[i]);
            $('#list').append(newLi);
        }
    }
    qGen();
    // correct answer
    function clickChoice(){
      $('#list').on("click",'button', function(){
          picked = parseInt($(this).attr("data-id")); 
          console.log(picked + "    click");
           if (picked === j){
               console.log(j + "    if");
               qcount++;
               x = questionList[qcount]; 
               j = x.answer;
               qGen();
               correct();
           }else{
               qcount++;
               incorrect();
                x = questionList[qcount]; 
               j = x.answer;
               qGen();
           }
    })
    }
    clickChoice();
    //timer
    function time(){
        timeLeft--;
        $('#time').text(timeLeft);
        if(timeLeft===0){
            $('#score').text('TIME UP');
             timer2 = setInterval(timeUp, 2000);
        }
    }
    //time up
    function timeUp(){
        clearInterval(timer);
        wrongQ++;
        qcount++;
        x = questionList[qcount]; 
        j = x.answer;
        clearInterval(timer2);
        nextQ();
    }
    //correct
    function correct(){
        clearInterval(timer);
        clearInterval(timer2);
        $("#list").text("");
        correctQ++;
        nextQ();
    }
    //incorrect
    function incorrect(){
        clearInterval(timer);
        clearInterval(timer2);
        $("#list").text("");
        wrongQ++;
        nextQ();
    }
    //next question gen
    function nextQ(){
        timeLeft= 10;
        $('#score').text("");
        $('#ca').text("");
        $('#question').text(x.q);
        //$("#time").text(timeLeft);
        $("#list").text("");
        qGen();
    }

Upvotes: 2

Views: 165

Answers (1)

Magnus
Magnus

Reputation: 7821

Below is a modified version of your code, which should solve your issues.

Notes

  • I have made some simple assumptions about your HTML and CSS
  • I have not implemented score updating, but that should be straight forward given the below

const questionList = [
  {
    q: 'What is a dog?',
    a: ['fish', 'mammal', 'plant', 'prokaryote'],
    answer: 1
  },
  {
    q: 'What is a cat?',
    a: ['mammal', 'fish', 'plant', 'amphibian'],
    answer: 0
  },
  {
    q: 'What is a tree?',
    a: ['plant', 'fish', 'mammal', 'none'],
    answer: 0
  },
  {
    q: 'What do cars run on?',
    a: ['gasoline', 'water', 'ethanol', 'liquid oxygen'],
    answer: 0
  },
  {
    q: 'What is 4 x 4?',
    a: ['8', '16', '4', '160'],
    answer: 1
  },
  {
    q: 'What is the capital of Australia?',
    a: ['Brisbane', 'GoldCoast', 'Perth', 'Canberra', 'Melbourne'],
    answer: 3
  },
  {
    q: 'What is the national flower of Canada?',
    a: ['sunflower', 'daisy', 'trillium', 'rose', 'lotus'],
    answer: 2
  }
];
//--------------------------------------
let picked;
let qcount = 0;
const output = [];
let timer;
const startingTime = 10;
let timeLeft;
let correctQ = 0;
let wrongQ = 0;
// var randomQ = Math.floor(Math.random()*7);
// let x = questionList[qcount];
// let j = x.answer;
// var cAns = x.a[j];
// console.log(cAns);
// console.log(j);

// next question gen
function nextQ() {
  timeLeft = 10;
  document.querySelector('#score').textContent = '';
  // document.querySelector('#ca').textContent = '';
  document.querySelector('#question').textContent = questionList[qcount].q;
  // $("#time").text(timeLeft);
  document.querySelector('#list').textContent = '';
  qGen();
}

// time up
function timeUp() {
  clearInterval(timer);
  wrongQ += 1;
  qcount += 1;
  nextQ();
}

// correct
function correct() {
  clearInterval(timer);
  correctQ += 1;
  nextQ();
}

// incorrect
function incorrect() {
  clearInterval(timer);
  wrongQ += 1;
  nextQ();
}

// timer
function time() {
  timeLeft -= 1;
  document.querySelector('#time').textContent = timeLeft;
  if (timeLeft === 0) {
    document.querySelector('#score').textContent = 'TIME UP';
    timeUp();
  }
}

// Add EventListener to each button
function addEL(el) {
  el.addEventListener('click', event => {
    picked = parseInt(event.currentTarget.getAttribute('data-id'), 10);
    console.log(`${picked} click`);
    const correctAnswer = questionList[qcount].answer;
    qcount += 1;
    if (picked === correctAnswer) {
      console.log(`${correctAnswer} if`);
      correct();
    } else {
      incorrect();
    }
  });
}

// new Q w/ options
function qGen() {
  const x = questionList[qcount];
  timeLeft = startingTime;
  document.querySelector('#time').textContent = startingTime;
  document.querySelector('#question').textContent = x.q;
  for (let i = 0; i < x.a.length; i += 1) {
    const newLi = document.createElement('li');
    const answer = document.createElement('button');
    answer.setAttribute('data-id', i);
    answer.classList.add('answer');
    answer.textContent = x.a[i];
    addEL(answer);
    newLi.appendChild(answer);
    document.querySelector('#list').appendChild(newLi);
  }
  timer = setInterval(time, 1000);
}

document.addEventListener('DOMContentLoaded', () => {
  qGen();
});
.answer {
  background-color: yellow;
}
<div>
  Question:
  <span id="question">XXX</span>
</div>
<div>
  Time:
  <span id="time">XXX</span>
</div>
<div>
  Score:
  <span id="score">XXX</span>
</div>
<div>
  <ul id="list">
  </ul>
</div>

Below are the changes I made, including some tips on good practices in JavaScript.

Code Logic

  • You are calling qGen twice, effectively spinning up two new intervals on each click
  • The event listener must be added to all the buttons
  • No need for the second timer
  • Only <li> elements are permitted inside <ul> / <ol>
    • It is perfectly fine to place other elements, like your buttons, inside those <li>s.
    • You can also format them differently, if you prefer (e.g. on one line)
  • Avoid operating on the DOM, before the DOM tree has finished loading
    • See the added DOMContentLoaded event listener
  • output, correctQ, wrongQ: These assigned values are never used in your code

Good Practices

  • Avoid unary operators ++ and --
  • Define functions before calling them
  • Don't use jQuery (or other abstractions) before you are completely comfortable with vanilla JavaScript
  • Prefer arrow functions over anonymous functions
    • Use event.currentTarget instead of this inside event listener

Upvotes: 1

Related Questions