My game works until summary page start link is clicked. Then I get undefined variables when trying to play again.
I'm new to JS so I'm sure it is a stupid user error and not handling scope correctly.
If I comment the location.reload at the bottom of the javascript it works just fine after the timer is up, but the coding assignment wants us to not do a refresh. I appreciate any help!
$(document).ready(function() {
var correctAnswers = 0;
var incorrectAnswers = 0;
var unansweredQuestions = 0;
var timeRemaining = 16;
var intervalID;
var indexQandA = 0;
var answered = false;
var correct;
var start = $(".start").html("Start Game");
start.on("click", startGame);
var triviaQandA = [{
question: "What is the fastest animal?",
answer: ["cheetah", "turtle", "giraffe", "elephant"],
correct: "0",
image: ("assets/images/circle.png")
}, {
question: "When you are blue you turn?",
answer: ["red", "green", "blue", "yellow"],
correct: "2",
image: ("assets/images/dot.jpg")
}];
function startGame() {
$(".start").hide();
correctAnswers = 0;
incorrectAnswers = 0;
unansweredQuestions = 0;
loadQandA();
}
function loadQandA() {
answered = false;
timeRemaining = 16;
intervalID = setInterval(timer, 1000);
if (answered === false) {
timer();
}
correct = triviaQandA[indexQandA].correct;
var question = triviaQandA[indexQandA].question;
$(".question").html(question);
for (var i = 0; i < 4; i++) {
var answer = triviaQandA[indexQandA].answer[i];
$(".answers").append("<h4 class = answersAll id =" + i + ">" + answer + "</h4>");
}
$("h4").click(function() {
var id = $(this).attr("id");
if (id === correct) {
answered = true;
$(".question").text("The answer is: " + triviaQandA[indexQandA].answer[correct]);
correctAnswer();
} else {
answered = true;
$(".question").text("You chose: " + triviaQandA[indexQandA].answer[id] + " the correct answer is: " + triviaQandA[indexQandA].answer[correct]);
incorrectAnswer();
}
console.log(correct);
});
}
function timer() {
if (timeRemaining === 0) {
answered = true;
clearInterval(intervalID);
$(".question").text("The correct answer is: " + triviaQandA[indexQandA].answer[correct]);
unAnswered();
} else if (answered === true) {
clearInterval(intervalID);
} else {
timeRemaining--;
$(".timeRemaining").text("You have " + timeRemaining);
}
}
function correctAnswer() {
correctAnswers++;
$(".timeRemaining").text("You are spot on!!!").css({
"color": "#3d414f"
});
reset();
}
function incorrectAnswer() {
incorrectAnswers++;
$(".timeRemaining").text("You are sooo wrong").css({
"color": "#3d414f"
});
reset();
}
function unAnswered() {
unansweredQuestions++;
$(".timeRemaining").text("You didn't choose anything...").css({
"color": "#3d414f"
});
reset();
}
function reset() {
$(".answersAll").remove();
$(".answers").append("<img class=answerImage width='150' height='150' src='" + triviaQandA[indexQandA].image + "'>");
indexQandA++;
if (indexQandA < triviaQandA.length) {
setTimeout(function() {
loadQandA();
$(".answerImage").remove();
}, 2000);
} else {
setTimeout(function() {
$(".question").remove();
$(".timeRemaining").remove();
$(".answerImage").remove();
$(".answers").append("<h4 class = answersAll end>Correct answers: " + correctAnswers + "</h4>");
$(".answers").append("<h4 class = answersAll end>Wrong answers: " + incorrectAnswers + "</h4>");
$(".answers").append("<h4 class = answersAll end>Unanswered questions: " + unansweredQuestions + "</h4>");
correctAnswers = 0;
incorrectAnswers = 0;
unansweredQuestions = 0;
// setTimeout(function() {
// location.reload();
// }, 5000);
var start = $(".start-over").html("Start Game");
start.on("click", startGame);
}, 2000);
}
};
});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div class="start"></div>
<h1>Trivia</h1>
<h5 class="timeRemaining"></h5>
<h3 class="question"></h3>
<div class="answers"></div>
<div class="start-over"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
Here's an inline link to my Codepen.
The problem comes from the fact that you increment indexQandA on each question, but never reset it. You need to reset it to 0 when starting a new game - that is, in the else clause of the reset function you need to put indexQandA=0 alongside where you reset the other variables to 0.
There is a further problem when this is done though, the quiz runs ok a second time now, but doesn't display the questions any more. That's because you've removed the .question element during reset. You don't need to do this, you can just hide it, then display it again when the game restarts.
Actually, it transpired there were further problems. I've solved it - run through the game about 5 times in a row with no obvious problems - with the following code (I make no claim that this is the correct code to do it, it's just minimal changes to your existing code to get it to work):
$(document).ready(function() {
var correctAnswers = 0;
var incorrectAnswers = 0;
var unansweredQuestions = 0;
var timeRemaining = 16;
var intervalID;
var indexQandA = 0;
var answered = false;
var correct;
var start = $(".start").html("Start Game");
start.on("click", startGame);
$(".start-over").on("click", startGame);
var triviaQandA = [{
question:"What is the fastest animal?",
answer:["cheetah","turtle","giraffe","elephant"],
correct:"0",
image: ("assets/images/circle.png")
}, {
question:"When you are blue you turn?",
answer:["red","green","blue","yellow"],
correct:"1",
image: ("assets/images/dot.jpg")
}];
function startGame() {
$(".summary").hide();
$(".start").hide();
$(".timeRemaining").show();
$(".question").show();
$(".answers").show();
$(".start-over").hide();
correctAnswers = 0;
incorrectAnswers = 0;
unansweredQuestions = 0;
loadQandA();
}
function loadQandA() {
answer = [];
answered = false;
timeRemaining = 16;
timer();
intervalID = setInterval(timer, 1000);
/*if (answered === false) {
timer();
}*/
correct = triviaQandA[indexQandA].correct;
var question = triviaQandA[indexQandA].question;
$(".question").html(question);
for (var i = 0; i < 4; i++) {
var answer = triviaQandA[indexQandA].answer[i];
$(".answers").append("<h4 class = answersAll id =" + i + ">" + answer + "</h4>");
}
$("h4").click(function() {
var id = $(this).attr("id");
if (id === correct) {
answered = true;
$(".question").text("The answer is: " + triviaQandA[indexQandA].answer[correct]);
correctAnswer();
} else {
answered = true;
$(".question").text("You chose: " + triviaQandA[indexQandA].answer[id] + " the correct answer is: " + triviaQandA[indexQandA].answer[correct]);
incorrectAnswer();
}
console.log(correct);
});
}
function timer() {
if (timeRemaining === 0) {
answered = true;
//clearInterval(intervalID);
$(".question").text("The correct answer is: " + triviaQandA[indexQandA].answer[correct]);
unAnswered();
} else if (answered === true) {
//clearInterval(intervalID);
} else {
timeRemaining--;
$(".timeRemaining").text("You have " + timeRemaining);
}
}
function correctAnswer() {
correctAnswers++;
$(".timeRemaining").text("You are spot on!!!").css({
"color": "#3d414f"
});
reset();
}
function incorrectAnswer() {
incorrectAnswers++;
$(".timeRemaining").text("You are sooo wrong").css({
"color": "#3d414f"
});
reset();
}
function unAnswered() {
unansweredQuestions++;
$(".timeRemaining").text("You didn't choose anything...").css({
"color": "#3d414f"
});
reset();
}
function reset() {
clearInterval(intervalID);
$(".answersAll").remove();
$(".answers").append("<img class=answerImage width='150' height='150' src='" + triviaQandA[indexQandA].image + "'>");
indexQandA++;
if (indexQandA < triviaQandA.length) {
setTimeout(function () {
loadQandA();
$(".answerImage").remove();
}, 2000);
} else {
setTimeout(function() {
$(".summary").show();
$(".question").hide();
$(".timeRemaining").hide();
$(".answers").hide();
$(".start-over").show();
$(".answerImage").remove();
$(".summary").append("<h4 class = answersAll end>Correct answers: " + correctAnswers + "</h4>");
$(".summary").append("<h4 class = answersAll end>Wrong answers: " + incorrectAnswers + "</h4>");
$(".summary").append("<h4 class = answersAll end>Unanswered questions: " + unansweredQuestions + "</h4>");
correctAnswers = 0;
incorrectAnswers = 0;
unansweredQuestions = 0;
indexQandA=0;
// setTimeout(function() {
// location.reload();
// }, 5000);
$(".start-over").html("Start Game");
start.on("click", startGame);
}, 2000);
}
};
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
<div class="start"></div>
<h1>Trivia</h1>
<h5 class="timeRemaining"></h5>
<h3 class="question"></h3>
<div class="answers"></div>
<div class="summary"></div>
<div class="start-over"></div>
</body>
</html>
There were 2 main problems with your code as it initially was:
most seriously, you were adding startGame as the click handler for the "start over" button each time the reset function was called. As a result, multiple such handlers were getting added by the time you played a 3rd, 4th, 5th... time, with the result that the startGame function was getting called multiple times, which was why you were getting the answer options printed multiple times. I've fixed this by removing that section of code entirely, and adding the event listener once, on page load.
your timer was also going a bit haywire, as you were calling the timer function manually as well as using setInterval to run it every second, and nor were you always clearing it when the question was answered. I've simplified this greatly, and made it work, just by setting the timer in loadQandA, as before, and clearing it in the reset function.
You will see that I've mainly just commented things out, rather than removed them - this will hopefully make it easier to see what I've removed.
Related
I want to make a piece of code redirect the user to a certain website after 15 is reached by a number. I would use the code below and it would not redirect even if the number is met.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<h1 id="text">please wait <span id="thing" style="font-size: 70px;">0</span> seconds<span id="wait"></span></h1>
<script>
var request = document.getElementById("thing");
var num = 0;
var goup = setInterval(function goUp() {
num++;
request.innerHTML = num;
}, 1000)
var dots = window.setInterval(function dots() {
var waiter = document.getElementById("wait");
if (waiter.innerHTML.length > 5) {
waiter.innerHTML = ""
} else {
waiter.innerHTML += ".";
}
}, 100)
if (num.innerHTML === 15) {
location.href('https://google.com/');
}
</script>
</body>
</html>
Try this:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<h1 id="text">please wait <span id="thing" style="font-size: 70px;">15</span> seconds<span id="wait"></span></h1>
<script>
var request = document.getElementById("thing");
var num = 15;
var goup = setInterval(function goUp() {
request.innerHTML = --num;
if (num === 0) {
window.location.href = 'https://google.com/';
}
}, 1000)
var dots = window.setInterval(function dots() {
var waiter = document.getElementById("wait");
if (waiter.innerHTML.length > 5) {
waiter.innerHTML = ""
} else {
waiter.innerHTML += ".";
}
}, 100)
</script>
</body>
</html>
I simply put the if statement inside the interval so it checks every time the 'timer' is updated. Also, I made the timer go backwards because you are telling the user to wait for 15 seconds, so it should be counting down. In addition, I changed location.href() to window.location.href =, since location.href() isn't a function, it's a string, so you need to change it using an assignment operator.
var request = document.getElementById("thing");
var num = 0;
var goup = setInterval(function goUp() {
request.innerHTML = num++;
if (num === 4) {
clearInterval(goup)
location.href = 'https://google.com/';
}
}, 1000)
var dots = window.setInterval(function dots() {
var waiter = document.getElementById("wait");
if (waiter.innerHTML.length > 5) {
waiter.innerHTML = ""
} else {
waiter.innerHTML += ".";
}
}, 100)
So, I'm very new, so apologies if this is a silly question. I have worked out a Trivia Quiz for my learning to code classes I'm taking. I want to display an image on the confirmation screen that shows whether the user was right or wrong. I think the code is correct-ish? I'm not sure what isn't working.
I left out the main Question and answer object for space. Sorry if I didn't format this ideally? I'm still kinda figuring out how things work here.
Here is my code for reference:
//array to help me iterate and insert images
var imgArray = ["question1", "question2", "question3", "question4", "question5", "question6", "question7", "question8", "question9", "question10", "question11", "question12", "question13"];
var currentQuestion = 0;
var win = 0;
var lose = 0;
var unanswered = 0;
var time = 30;
//set up divs to contain our info
var rightDiv = $("<div class='rightAns'></div>");
var timerDiv = $("<div class='countdown'><h3></h3></div>");
var questionDiv = $("<div class='question'><h1></h1></div><br>");
var answerDiv = $("<div class='answers'></div>");
//object keys to return questions in order
var keys = Object.keys(questions);
var key = keys[n];
var n = 0;
//function to setup and restart game
function reset() {
$("#start-button").hide("slow");
$("#start-here").hide("slow");
win = 0;
lose = 0;
unanswered = 0;
n = 0;
key = keys[n];
currentQuestion = 0;
$("#question-block").empty();
var reset = function () {
time = 30;
$(".rightAns").empty();
$(".rightAns").remove();
// $("#image").empty();
$("#time-remaining").append(timerDiv);
$(".countdown h3").html("Time Remaining: " + time);
$("#question-block").append(questionDiv);
$("#question-block").append(answerDiv);
}
reset();
//function to show questions
function showQuestion() {
$(".question h1").html(questions[key].question);
for (var i = 0; i < questions[key].answers.length; i++) {
$(".answers").append("<button class='answer btn btn-danger btn-lg m-1'>" + questions[key].answers[i] + "</button>");
}
$(".answers button").on("click", function () {
var selected = $(this).text();
//if then to check question correctness
if (selected === questions[key].correct) {
clearInterval(counter);
$(timerDiv).remove();
$(questionDiv).remove();
$(".answers button").remove();
$(answerDiv).remove();
$("#correct-answer").append(rightDiv);
$(".rightAns").text("That's Correct!!");
$("#image").html('<img src = ".assets/images/' + imgArray[currentQuestion] + '" width = "400px">');
win++;
currentQuestion++;
} else {
clearInterval(counter);
$(timerDiv).remove();
$(questionDiv).remove();
$(".answers button").remove();
$(answerDiv).remove();
$("#correct-answer").append(rightDiv);
$(".rightAns").text("Nope! The correct answer was: " + questions[key].correct);
$("#image").html('<img src = ".assets/images/' + imgArray[currentQuestion] + '" width = "400px">');
lose++;
currentQuestion++;
}
n++;
key = keys[n];
//checking to see if there are more questions left
if (checkForLast()) {
finalScore();
} else {
setTimeout(countReset, 3 * 1000);
setTimeout(reset, 3 * 1000);
setTimeout(showQuestion, 3 * 1000);
}
});
}
showQuestion();
var counter = setInterval(count, 1000);
//show time remaining for each question
function count() {
time--;
$(".countdown h3").html("Time Remaining: " + time);
if (time < 1) {
clearInterval(counter);
$(timerDiv).remove();
$(questionDiv).remove();
$(".answers button").remove();
$("#correct-answer").append(rightDiv);
$(".rightAns").html("You took too long! The correct answer was: " + questions[key].correct);
unanswered++;
n++;
key = keys[n];
if (checkForLast()) {
finalScore();
} else {
setTimeout(countReset, 3 * 1000);
setTimeout(reset, 3 * 1000);
setTimeout(showQuestion, 3 * 1000);
}
}
}
function checkForLast() {
if (key === undefined) {
return true;
}
return false;
}
//timer for the message after you choose your answer
function countReset() {
counter = setInterval(count, 1000);
}
//showthe final score screen
function finalScore() {
$(".rightAns").remove();
$("#image").empty();
$("#question-block").prepend("<h2>Unanswered: " + unanswered + "</h2>");
$("#question-block").prepend("<h2>Incorrect: " + lose + "</h2>");
$("#question-block").prepend("<h2>Correct: " + win + "</h2>");
$("#start-button").show();
$("#start-here").show();
}
};
//function to start game on button click
$(document).on("click", "#start-button", reset);
});
After tinkering for a bit, I dropped the "." at the beginning of the call and added the .jpg to the section after imgArray[currentQuestion]. That solved it. Thanks for the suggestions.
The script must have to print 'Hello', then 'Good bye', because of the entries on function call. But only prints once. Why?
What's wrong here bellow.
PD: Now it doesn't work. It does if i comment the recursion call line
<html>
<head>
</head>
<body>
<script type="text/javascript">
function writing(i,first,second) {
len=arguments.length;
if (i<=len) {
current=arguments[i];
c=0;
inter=setInterval(function() {
if (c>=current.length) {
clearInterval(inter);
} else {
field=document.getElementById('div1');
field.innerHTML+=current[c];
c+=1;
}
},200);
}
i<len?writing(i+1,first,second):writing(i=0,first,second);
}
writing(1,'Hello','Good bye');
</script>
<div id="div1"></div>
</body>
There are so many problems with the code , first was it was infinite loop (never ending) , second was variable declaration , and others...
Here I have attached the snippet , please run and check, if its that you are looking for.
I have to add setTimeout for fullfill your requirement.
var interval_counter = 0;
function writing(i, first, second) {
var len = arguments.length;
if (i != 0 && i <= len) {
var current = arguments[i];
var c = 0;
setTimeout(function() {
var inter = setInterval(function() {
if (c >= current.length) {
clearInterval(inter);
} else {
field = document.getElementById('div1');
field.innerHTML += current[c];
c += 1;
}
}, 200);
}, 200 * interval_counter);
interval_counter = interval_counter + current.length;
i < (len - 1) ? writing(i + 1, first, second) : writing(i = 0, first, second);
} else {
return false;
}
}
writing(1, 'Hello', 'Good bye');
<div id="div1"></div>
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 5 years ago.
I wrote simple JQuery function to count from 0 to specified number in a range of divs with certain class name:
<div class="number">50%</div>
<div class="number">75%</div>
this is JQuery code:
$(document).ready(function () {
$(".number").each( function() {
var percent = $(this).text().replace("%","");
console.log("test " + percent);
function countToPercent(percent) {
var interval = setInterval(counter,25);
var n = 0;
function counter() {
if (n >= percent) {
clearInterval(interval);
}
else {
n += 1;
// console.log(n);
$(this).text(n + "%");
}
}
}
countToPercent(percent);
})
});
unfortunetly it isn't working now - there is probably a problem within this is line:
$(this).text(n + "%")
i debbuged it using console and commented console.log(n) works - it counts from 0 to specified number. so it has to be problem with selecting correct DOM element.
is it forbidden to use setInterval() with .each() method or with "this" keyword? or is there some bug in the code?
Your problem is that this keyword does not exist in context of interval scope.
Wrap interval stuff to another scope by providing injecting variables and call it:
$(function () {
$(".number").each( function() {
var $this = $(this);
var percentage = parseFloat($this.text().replace("%",""));
console.log("test " + percentage);
(function(percentage, jqObject) {
var n = 0;
var interval = setInterval(function() {
if(n >= percentage) return clearInterval(interval);
n++;
jqObject.text(n + '%');
}, 25);
})(percentage, $this);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="number">50%</div>
<div class="number">75%</div>
or You may have external function that does same thing:
$(function () {
function animatePercentageProgress(percentage, jqObject) {
var n = 0;
var interval = setInterval(function() {
if(n >= percentage) return clearInterval(interval);
n++;
jqObject.text(n + '%');
}, 25);
}
$(".number").each( function() {
var $this = $(this);
var percentage = parseFloat($this.text().replace("%",""));
console.log("test " + percentage);
animatePercentageProgress(percentage, $this);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="number">50%</div>
<div class="number">75%</div>
or more "functional way":
$(function () {
function animatePercentageProgress(idx, jqObjectPointer) {
var jqObject = $(jqObjectPointer);
var percentage = parseFloat(jqObject.text().replace("%",""));
var n = 0;
var interval = setInterval(function() {
if(n >= percentage) return clearInterval(interval);
n++;
jqObject.text(n + '%');
}, 25);
}
$(".number").each(animatePercentageProgress);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="number">50%</div>
<div class="number">75%</div>
You need to save a context to use it in setInterval
$(document).ready(function () {
$(".number").each( function() {
var percent = $(this).text().replace("%","");
console.log("test " + percent);
var self = this; // fixing context;
function countToPercent(percent) {
var n = 0;
var interval = setInterval(() => {
if (n >= percent) {
clearInterval(interval);
}
else {
n += 1;
// console.log(n);
$(self).text(n + "%");
}
},25);
}
countToPercent(percent);
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="number">50%</div>
<div class="number">75%</div>
As a java script beginner, I wanted to try my hand at writing stop watch code and I wrote the following:
<!DOCTYPE html>
<html>
<body>
<p>A script on this page starts a stopwatch:</p>
<p id="demo"></p>
<button id="start-stop" onclick="myTimerFunction()">Start time</button>
<button id="resetter" style="visibility:hidden" onclick="resetTimer()">Reset</button>
<script>
var timer = new Object();
timer.hours = 0;
timer.minutes = 0;
timer.seconds = 0;
timer.milliseconds = 0;
timer.add = add;
function add() {
timer.milliseconds+=10;
if(timer.milliseconds == 1000) {
timer.seconds++;
timer.milliseconds = 0;
}
if(timer.seconds == 60) {
timer.minutes++;
timer.seconds = 0;
}
if(timer.minutes == 60) {
timer.hours++;
timer.minutes = 0;
}
}
timer.display = display;
function display () {
var str = "";
if(timer.hours<10) {
str += "0";
}
str += timer.hours;
str += ":";
if(timer.minutes<10) {
str += "0";
}
str += timer.minutes;
str += ":";
if(timer.seconds<10) {
str += "0";
}
str += timer.seconds;
str += ":";
/*var x = timer.milliseconds/10;
if(x < 10) {
str += "0";
}*/
if(timer.milliseconds<10) {
str += "0";
}
if(timer.milliseconds<100) {
str += "0";
}
str += timer.milliseconds;
return str;
}
timer.reset = reset;
function reset() {
timer.hours = 0;
timer.minutes = 0;
timer.seconds = 0;
timer.milliseconds = 0;
}
var myVar;
function start() {
timer.add();
var d = new Date();
var t = d.toLocaleTimeString();
document.getElementById("demo").innerHTML = timer.display() + "\t" + t;
}
function stop() {
clearInterval(myVar);
}
function resetTimer() {
stop();
timer.reset();
document.getElementById("demo").innerHTML = timer.display();
document.getElementById("start-stop").innerHTML="Start time";
document.getElementById("resetter").style.visibility="hidden";
}
function myTimerFunction() {
var x = document.getElementById("start-stop");
if(x.innerHTML.match("Start time")) {
document.getElementById("resetter").style.visibility="visible";
myVar = setInterval(function(){start()},10);
x.innerHTML="Stop time";
}
else if(x.innerHTML.match("Stop time")) {
stop();
x.innerHTML="Start time";
}
}
</script>
</body>
</html>
But, the problem is when I put the delay in setInterval(func,delay) as 1 and doing corresponding changes, it is not giving reliable timing for seconds. It is slower than a normal clock. It gives 'kind of' reliable timing for delay >= 10.
I checked for stop watch js scripts online but all of them use some or other form of Date() and set "delay" as "50", which I do not understand why, as of now. There is an answer here in SO which doesn't use Date() but it also has the same problem as mine. I could not comment there as I do not have enough reputation so I am asking a question instead.
So, my question is: Is it impossible to achive normal clock reliability, if we don't use Date() function? Else if it is possible, please help me improve this piece of code or please provide some pointers.
Thanks.
Here's how you'd do it without getTime, which you really shouldn't...
var ms = 0;
var intervalID;
function start() {
var freq = 10; // ms
intervalID = setInterval(function () {
ms += 10;
var myDate = new Date(ms);
document.getElementById('watch').innerHTML = myDate.getUTCHours() + ":" + myDate.getMinutes() + ":" + myDate.getSeconds() +
":" + myDate.getMilliseconds();
}, freq);
}
function stop() {
clearInterval(intervalID);
}
function reset() {
ms = 0;
myDate = new Date(ms);
document.getElementById('watch').innerHTML = myDate.getUTCHours() + ":" + myDate.getMinutes() + ":" + myDate.getSeconds() +
":" + myDate.getMilliseconds();
}
Fiddle
As you've found out setInterval/setTimeout is not reliable. You must use a native time library to get a reliable time.
Since you can't keep the time in JavaScript the idea is that you poll the time, and poll it often so that it looks close enough.
If you naively do:
setInterval(function () {
console.log((new Date()).getTime();
}, 1000); // 1 second
you will see that it will skip seconds.
A better approach is something like:
var last = 0;
setInterval(function () {
var now = Math.floor((new Date()).getTime() / 1000); // now in seconds
if (now !== last) {
console.log(now);
last = now;
}
}, 10); // 10ms
If you want more information as too why JavaScript timers are unreliable, read this great article.
http://ejohn.org/blog/how-javascript-timers-work/