I'm using MVC with some partial rendering and ajax calls. In one of my result tables I have a field that is a timer.
The following code is on my partial view that is rendered every 30 seconds. One of the fields is an active timer that displays to the user:
<td><div id="#someID" class="nobutton"></div><script>setInterval(function () { startTimer('#startDateTime', '#someID') }, 500);</script></td>
This works great but after the ajax call is made to refresh the partial, the interval function is still calling the old IDs (someID) that are no longer there.
Here is the javascript for the timer:
<script type="text/javascript">
function startTime(dt, field){
var field = document.getElementById(field);
if (field == null)
return;
var rowTime = new Date(dt);
var rowTimems = rowTime.getTime(rowTime);
var currentTime = new Date();
var currentTimems = currentTime.getTime(currentTime);
var difference = currentTimems - rowTimems;
var hours = Math.floor(difference / 36e5),
minutes = Math.floor(difference % 36e5 / 60000),
seconds = Math.floor(difference % 60000 / 1000);
field.innerHTML = formatTime(hours) + ":" + formatTime(minutes) + ":" + formatTime(seconds);
</script>
And the call on the main page that refreshes the partial is pretty simple:
var t = setInterval(function () {
$.ajax({
url: "MYURL",
type: "GET",
cache: false,
success: function (data) {
$("#summary").html(data);
}
});
}, 30000);
Is there a way to kill the old timers? Why are they still running?
The reason that code is still executing is that the interval was never stopped. Try this in your partial instead. It will check if the previous interval timer is present and clear it.
<td><div id="#someID" class="nobutton"></div>
<script>
if( window.prtInt != void 0 ) clearInterval(window.prtInt);//void 0 means undefined
window.prtInt = setInterval(function () { startTimer('#startDateTime', '#someID') }, 500);
</script></td>
If you wish to clear out all the timers on the entire page (may have undesirable affects) then you may get the integer for a new timer, and loop up to that number clearing intervals that are still present.
<script>
function clrTimers(){
var clr = setTimeout(function(){},10);
for( var i = 0; i < clr; i++ ){
clearInterval(i);
}
}
</script>
If you wish to skip some, then you could include them with the continue keyword if you knew them. Such as
for( var i = 0; i < clr; i++ ){
if( i == t ) continue; //I believe t was your ajax interval variable
clearInterval(i);
}
setInterval(function () { startTimer('#startDateTime', '#someID') }, 500);
Will continue to execute with the same values until it is cleared with clearInterval(). In order to clear it you need to save it a var and feed that to the clearInterval() function.
Related
On clicking a button with id next-problem , an AJAX request is send to server and new task get loaded. I want to restart the timer when new task get loaded. In AJAX , on success I want to reset timer. But the problem is many setInterval get starts on click the button. How to avoid that ?
var timer;
var countdownfunction;
var countDownTime;
timer = function (){
clearInterval(countdownfunction);
countDownTime = $("#timer").attr("data-timer-val");
var countdownfunction = setInterval(function() {
if(countDownTime < 10 ){
var temp = "0" + countDownTime;
$("#time").text(temp);
}else{
$("#time").text(countDownTime);
}
countDownTime = countDownTime - 1;
if (countDownTime < 0) {
clearInterval(countdownfunction);
$("#time-expired").text("Please click next problem.");
}
}, 1000);
} ;
$(document).ready(function(){
timer();
});
$(document).ready(function(){
$("#next-problem").on('click', function(){
$.ajax({
type : 'POST',
url : '/test/next-problem/',
success : function(data){
// console.log(data);
$("#problem-content").html(data);
clearInterval(countdownfunction);
timer();
},
});
});
});
Remove var from the following line:
var countdownfunction = setInterval(function() {
Because of this var you have two differently scoped values of countdownfunction so when you call clearInterval you are never clearing this particular functionally-scoped value until that timer reaches -1, so you may have multiple timers running on top of each other.
Once you remove var here, you now have a single, globally-scoped variable that can be cleared and reassigned a value.
I am working on a personal project and I am making use of JS to implement a simple countdown timer. There are several requirements that need to be addressed:
- The timer starts when the page is loaded
- When the user wishes to leave the page, they are warned that their progress will be lost, hence, the timer is reset.
I am making use of localStorage to keep the remaining time, since every time the user submits a form that is on the page, the page is reloaded (I understand that it is better to use AJAX, but for the time being, I am trying to make it work properly as it is). You can see my code below:
var submit_form;
var timeLeft;
function startTimer(duration, content) {
var timer = duration, min, sec;
setInterval(function () {
if (--timer < 0) {
//redirect here;
submit_form = true;
localStorage.removeItem("remainingTime");
window.location.replace("some url");
} else {
min = parseInt(timer / 60, 10);
sec = parseInt(timer % 60, 10);
min = min < 10 ? "0" + min : min;
sec = sec < 10 ? "0" + sec : sec;
localStorage.setItem("remainingTime", timer);
console.log(timer);
content.textContent = min + ":" + sec;
}
}, 1000);
}
window.onload = function () {
submit_form = false;
console.log(submit_form);
if (localStorage.getItem("remainingTime")) {
timeLeft = localStorage.getItem("remainingTime");
} else {
timeLeft = 60;
}
var display = document.querySelector('#time');
startTimer(timeLeft, display);
};
window.onbeforeunload = function () {
if (submit_form != true) {
localStorage.removeItem("remainingTime");
window.localStorage.clear();
return "You will lose all your progress!/nAre you sure you want to quit?";
}
};
'submit_form' is used for controlling when 'onbeforeunload' gets triggered - When the user submits the form, it shouldn't trigger.
<button type="submit" name="submit" value="Search" id="search-button" onclick="submit_form = true;">Submit</button>
The issue that I am facing, which is killing me, is that sometimes when the user tries to leave the page, i.e. 'onbeforeunload' is triggered, the localStorage doesn't always clear. Therefore, when they come back to the page, the timer doesn't start from the beginning, but instead, from where they left off.
I have tried a few variations of the code above, and my best guess is that setInterval() might be the reason for localStorage not clearing every time.
I'm making a simple punch in / punch out timer.
Problem im facing is that it works perfectly on page load but when the user clicks end_work_btn then begin_work_btn, the timer kinda stacks the initial value and the new one starting from 0 so it's trying to display both. It keeps stacking everytime they click the buttons until page reload when it resets.
I've done a bunch of reading and figured clearInterval(timer) would do it but no go so assuming i'm not using it correctly or i'm way off the ball on whats wrong here.
Here's what I got so far
<button id="begin_work_btn">Begin Work</button>
<button id="end_work_btn"><span id="hours"></span>:<span id="minutes"></span>:<span id="seconds"></span></button>
<script>
var emp_id = '2';
var timer = null;
function reset_timer(time){
var sec = time;
clearInterval(timer);
function pad ( val ) {
return val > 9 ? val : "0" + val;
}
var timer = setInterval( function(){
$("#seconds").html(pad(++sec%60));
$("#minutes").html(pad(parseInt(sec/60,10)%60));
$("#hours").html(pad(parseInt(sec/3600,10)));
}, 1000);
}
reset_timer(<?php echo $existing_time;?>);
$("#begin_work_btn").click(function(){
$.ajax({
type: "post",
url: "<?php echo $url;?>process.php",
data: "agent_id="+emp_id+"&action=begin_work",
success: function(data){
var sec = 0;
reset_timer(sec);
$('#begin_work_btn').hide();
$('#end_work_btn').show();
}
});
});
$("#end_work_btn").click(function(){
$.ajax({
type: "post",
url: "<?php echo $url;?>process.php",
data: "agent_id="+emp_id+"&action=end_work",
success: function(data){
$('#end_work_btn').hide();
$('#begin_work_btn').show();
}
});
});
</script>
Pretty simple, scope is different. In your code you have two variables named timer. The one inside the function is created each time reset_timer is called. One outside never gets a value. So each time you call the function, another instance of timer is created.
var timer = null; //<-- outside
function reset_timer(time){
clearInterval(timer);
var timer = setInterval( ... , 1000); //<--redefined inside so each time, it creates new variable
}
You basically have this:
function reset_timer(time){
var timer;
clearInterval(timer);
timer = setInterval( ... , 1000);
}
It should not be defined with var. This way it updates the variable defined outside of the function each time it is called.
var timer = null;
function reset_timer(time){
if (timer) clearInterval(timer);
timer = setInterval( ... , 1000); //<--no more var
}
I have a page where I fire an ajax request AND show a loading sign for the first 6 seconds after the user presses a button :
<button onclick="runLoader();"></button>
<script>
var startTime=(new Date).getTime();
function runLoader(){
runAjax();
var i=0;
var timer = setInterval(function(e){
i++;
//code for loading sign
if(i==3)
clearInterval(timer);
} ,2000);
}
function runAjax(){
$.ajax({
//
}).done(function(){
var timer2 = setInterval(function(){
var d = new Date();
var t = d.getTime();
if(t-startTime>=6000){
clearInterval(timer2);
// do X
}
},500);
}
}
</script>
I want to do action X only after both runLoader() has run for 6 seconds and runAjax() has resulted in a response, and no sooner.
Like, if runAjax() responds in 2 seconds, I still want to continue showing loading sign for 6 seconds and then perform X.
And if the loading sign has shown for 6 seconds, I want to wait for runAjax() to return for as long as it takes.
But using the Date() method is giving inaccurate results. For eg : It shows 7.765 s elapsed even when only 2 s have passed. I read somewhere I should use console.log(time) for better accuracy, but it doesnt work in <=IE9.
Is there a better way to approach this problem ?
Note: I am using setInterval() instead of setTimeout() because the loading involves cycling through an array of 3 elements, "Fetching", "Processing" and "Loading" each shown for 2 seconds :)
I would use deferreds and $.when:
function start(){
$.when(runLoader(), runAjax()).done(function() {
//both are now finished
});
}
function runLoader() {
//show loader here
var def = $.Deferred();
setTimeout(function() {
//hide loader here
def.resolve(true);
}, 6000);
return def.promise();
}
function runAjax() {
var def = $.Deferred();
$.ajax({...}).done(function(result) {
//handle response here
def.resolve(true);
});
return def.promise();
}
I would set a flag to mark it as "ready". There may be better ways to handle this, but this is just off the top of my head.
<script>
function runLoader(){
runAjax();
var i=0;
var timer = setInterval(function(e){
i++;
//code for loading sign
if(i==3)
clearInterval(timer);
} ,2000);
}
function runAjax(){
var timeElapsed = false;
setTimeout(function(){timeElapsed = true}, 6000);
$.ajax({
//
}).done(function(){
var timer2 = setInterval(function(){
if(timeElapsed){
clearInterval(timer2);
// do X
}
},500);
}
}
</script>
You can create a pre-caller function that is run on runLoader() and as callback of runAjax(), that will verify if the other action is complete, and then do action X. Example:
var ajaxDone = false;
var loaderDone = false;
function doActionX() {
//your action happens here
}
function tryToDoX() {
if (ajaxDone && loaderDone) {
doActionX();
}
}
function runLoader(){
loaderDone = false;
runAjax();
//show loading sign
setInterval(function(e){
//hide loading sign
clearInterval(timer);
loaderDone = true;
tryToDoX();
}, 6000);
}
function runAjax(){
ajaxDone = false;
$.ajax({
//whatever
}).done(function(){
ajaxDone = true;
tryToDoX();
}
}
It isn't necessary to make a recurring timeout and poll both statuses, because they only get completed once (in every run, i.e. booleans aren't set to false and true while waiting).
EDIT: This approach can be used to any asynchronous code that doesn't change status intermitently, even without jQuery.
I have a form which does an AJAX action when submitting. Just after a user has submitted the form, I'd like to impose a minimal waiting time of 5sec to post one more time.
What the better way ? I made this script (doesn't work but I guess idea is here):
$("form#submit_under_wall"+array[1]).submit(function() {
var submit_timestamp=0;
var c = new Date();
var now = ISODateString(c);
if(now-submit_timestamp>5){
var d = new Date();
var timestamp_message = ISODateString(d);
submit_timestamp = timestamp_message ;
//AJAX ACTION
}else{ alert("Wait 5sec between each post."); }
return false;
});
This doesnt work because the timestamp is reset every time they submit, you need to move submit_timestamp outside of the function. Also, that 5 should probably be 5 * 1000
something like this (not tested):
var submitTime = (new Date()).getTime() - 5000; //let user submit if he clicks within 5s of page load
$("form#submit_under_wall"+array[1]).submit(function(e) {
var now = (new Date()).getTime();
if(now-submitTime > 5000){
submitTime = (new Date()).getTime();
//AJAX ACTION
} else {
alert("Wait 5sec between each post.");
}
e.preventDefault();
});
The easiest and most standard way would be to use Javascript native setTimeout method.
var last_action;
function ajax_action() {
last_action = Date.getTime();
// AJAX STUFF
}
$("form#submit_under_wall"+array[1]).submit(function(e) {
e.preventDefault(); // Sexier than 'return false'
if(last_action != undefined) {
setTimeout(ajax_action, Date.getTime()-last_action); // Wait for 5s before doing the action
} else {
ajax_action();
}
});
use window.setTimeout()
Wrap what you want to do in a function (eg function foo (){ alert('test'); })
Call it with a 5 second delay setTimeout(foo,5000)