Trouble with multiple age counters (timers) - javascript

I have a page where I want to have "age counters" for bids put in by users. The number of users will vary from situation to situation, so that needs to be taken into consideration. I wrote this:
function timer(i) {
// this selects a 'hh:mm:ss' timestamp
if ($("#time_0" + i).text() !== "") {
var now = new Date();
var date = now.toDateString();
var tStamp = new Date(date + "," + $("#time_0" + i).text());
var diff = now - tStamp;
var mins = Math.floor(diff / (1000 * 60));
var secs = Math.floor((diff / 1000) % 60);
if (mins < 10) {
mins = "0" + mins;
}
if (secs < 10) {
secs = "0" + secs;
} else if (secs == 60) {
secs = "00";
}
$("#user" + i + "-t").text(mins + ':' + secs);
}
}
$(document).ready(function() {
//
var ids = [];
$("td[id^='time_0']").each(function() {
var i = ($(this).attr("id")).slice(-1);
ids.push(i);
});
for (i in ids) { // in my example ids = [1,2,3]
setInterval(function() {timer(i);}, 1000);
}
});
The timer itself functions just as I want it to, but only for user #2 (the middle one). I thought that if I encountered this problem, it would be either the first or last user in the list that had a working timer, but I'm getting blank cells for users #1 and #3.
Does anyone have any ideas as to how I can fix this? Thank you for your time.
==Edit==
I made a bare-bones jsfiddle

In your version loop never went past first reference, which was timer(0), because you called your function with timer(i), which was calling the first key in the array ids. When you have setInterval in a loop it will keep looping the first setInterval until that is terminated. By putting that i in an anonymous function, each setInterval gets fired.
$(document).ready(function () {
var ids = [];
$("td[id^='time_0']").each(function () {
var i = $(this).attr("id").slice(-1);
ids.push(i);
});
for (i in ids) {
(function(i) { // i needs to be stored into a anonymous function otherwise it gets overwritten
setInterval(function() {
timer(ids[i]); // calling just timer(i) would use array keys, not the actual values - ids[i] means: "give me the value of the key of i"
}, 1000)
})(i);
}
});
Made changes to your Fiddle

Related

Cannot get the chat timestamp to update automatically

I have the following code and cannot get the chat timestamp to update without refresh. I am new to javascript.
function timeSince(date) {
var seconds = Math.floor((new Date() - date) / 1000);
var interval = seconds / 31536000;
if (interval > 1) {
return Math.floor(interval) + " years";
}
interval = seconds / 2592000;
if (interval > 1) {
return Math.floor(interval) + " months";
}
interval = seconds / 86400;
if (interval > 1) {
return Math.floor(interval) + " days";
}
interval = seconds / 3600;
if (interval > 1) {
return Math.floor(interval) + " hours";
}
interval = seconds / 60;
if (interval > 1) {
return Math.floor(interval) + " minutes";
}
return Math.floor(seconds) + " seconds";
}
setInterval(timeSince, 1000);
var aDay = 24*60*60*1000;
console.log(timeSince(new Date(Date.now()-aDay)));
console.log(timeSince(new Date(Date.now()-aDay*2)));
const dates = new Date(message.timestamp)
if (message.user === currentUser) position = 'right';
const messageItem = `
<div class="message ${position}">
<span class="small">${timeSince(dates)}</span><br>
I tried: setInterval(timeSince, 1000); and setInterval(timeSince(), 1000); and setInterval(timeSince(date, 1000); and tried each in different place within the function, can't get to work.
timeSince just returns your formatted string with the duration. Setting an interval on this function will do practically nothing. You have to use the return value.
setInterval(function(){
var time = timeSince(message.timestamp);
//You can use a better selector here,
//but without more information, this is the best I can do.
document.getElementsByClassName("small")[0].innerHTML = time;
}, 1000);
found a half solution. I had to insert the setInterval under the function that calls the function under which sits the timesince function. Now the whole thing updates after the interval i set and not just the time, but setting an update interval of 1 minute makes it fine with me.
Apparently since I have several functions under one, the setInterval doesn't work, or didn't work in my case.
draw message is the function under which the timeSince function resides
Maybe coz i didn't provide this info here before i didn't get the right help
Solution:
this:
function getConversation(recipient) {
setInterval(function(){
$.getJSON(`/api/v1/message/?target=${recipient}`, function (data) {
messageList.children('.message').remove();
for (let i = data['results'].length - 1; i >= 0; i--) {
drawMessage(data['results'][i]);
}
messageList.animate({scrollTop: messageList.prop('scrollHeight')});
});
}, 60000);}
or this:
function getConversation(recipient) {
$.getJSON(`/api/v1/message/?target=${recipient}`, function (data) {
messageList.children('.message').remove();
for (let i = data['results'].length - 1; i >= 0; i--) {
drawMessage(data['results'][i]);
}
**setInterval(getConversation(recipient), 1000);**
messageList.animate({scrollTop: messageList.prop('scrollHeight')});
});
}
Thank you David for your help

Onclick change HTML5 localstorage value

I'm using count down timer by this code. If the page refresh or reload the count should not be reset for that I'm using this from localstorage. If there any alternate solution for this means Please suggest me.
var hms = $(".div__time .total_time").text();
var a = hms.split(':');
var hrs_min_sec = (+a[0]) * 60 * 60 + (+a[1]) * 60 + (+a[2]);
var time_hrs_min_sec = hrs_min_sec;
if (localStorage.getItem("counter")) {
if (localStorage.getItem("counter") <= 0) {
var value = time_hrs_min_sec;
} else {
var value = localStorage.getItem("counter");
}
} else {
var value = time_hrs_min_sec;
}
document.getElementById('overall_time').innerHTML = value;
var counter = function() {
if (value <= 0) {
localStorage.setItem("counter", time_hrs_min_sec);
} else {
value = parseInt(value) - 1;
console.log(value);
localStorage.setItem("counter", value);
}
document.getElementById('overall_time').innerHTML = value;
if (value == 0) {
// var redirect_url = "<?php echo site_url('home'); ?>";
// window.location.href = redirect_url;
}
var hours = Math.floor(value / 3600);
var minutes = Math.floor(value % 3600 / 60);
var seconds = Math.floor(value % 3600 % 60);
var red_time = hours + ' : ' + minutes + ' : ' + seconds;
document.getElementById('overall_times').innerHTML = red_time;
};
var interval = setInterval(function() {
counter();
}, 1000);
#overall_time {
display: none;
}
.div__time,
.total_time,
#overall_times {
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="div__time">
<div id="overall_time"></div>
<div id="overall_times"></div> /
<div class="total_time">
00:00:10
</div>
</div>
<button onclick="start_over_all_time(this);" id="over_all_time">Over All Time</button>
This one working fine.
When I click a button I need to reset the countdown value to 0. For example if countdown time counting from 10 to 0 if click a button at count 5. then the count has to be reset to 0. This point only not working for me.
I'm using this code for reset the localstorage value
function start_over_all_time(button) {
var inputElemnets = '0';
localStorage.setItem("value", inputElemnets);
console.log(value);
}
Fiddle Link
Thanks in Advance.
Okay... To objective here is to make the countdown "set to zero" button working.
Since SO snippet do not allow localStorage, the working scrip is on CodePen.
The main issue was not having declared the value at global scope.
See explanations within the code.
// I removed the 3 lines below because that was the only use of jQuery afer all...
// And because the math is weird to read (and incorrect).
//var hms = $(".div__time .total_time").text();
//var a = hms.split(':');
//var hrs_min_sec = (+a[0]) * 60 * 60 + (+a[1]) * 60 + (+a[2]);
// Replaced by this:
var hms = document.querySelector(".total_time").innerHTML;
var hms_arr = hms.split(":");
var time_hrs_min_sec = (hms_arr[0]*3600) + (hms_arr[1]*60) + hms_arr[2];
// Declare the "value" used in almost all functions at the global scope.
var value;
if (localStorage.getItem("counter")) {
if (localStorage.getItem("counter") <= 0) {
value = time_hrs_min_sec; // Removed the var
} else {
value = localStorage.getItem("counter"); // Removed the var
}
} else {
value = time_hrs_min_sec; // Removed the var
}
document.getElementById('overall_time').innerHTML = value;
var counter = function() {
if (value <= 0) {
localStorage.setItem("counter", time_hrs_min_sec);
} else {
value = parseInt(value) - 1;
console.log(value);
localStorage.setItem("counter", value);
}
document.getElementById('overall_time').innerHTML = value;
if (value == 0) {
// var redirect_url = "<?php echo site_url('home'); ?>";
// window.location.href = redirect_url;
}
var hours = Math.floor(value / 3600);
var minutes = Math.floor(value % 3600 / 60);
var seconds = Math.floor(value % 3600 % 60);
var red_time = hours + ' : ' + minutes + ' : ' + seconds;
document.getElementById('overall_times').innerHTML = red_time;
};
var interval = setInterval(function() {
counter();
}, 1000);
// Use value here... Instead of inputElemnets
function start_over_all_time(button) {
value = 0;
localStorage.setItem("counter", value);
console.log(value);
// Be cool with the browser and stop the interval.
setTimeout(function(){
clearInterval(interval);
},1000);
}
Now the set to zero button works... because you use the value variable everywhere. So as soon as you set it to zero, in start_over_all_time(), the next iteration of the interval will do the rest.
There is plenty other things to fix... But that was your question.
When it comes to caching user data, you have 2 options:
HTML5 storage (no backend required) => LocalStorage or SessionStorage
Cookies (backend required)
LocalStorage or SessionStorage
Instead of LocalStorage you could use SessionStorage. The difference is, that it lives only as long as the browser session is open, i.e. closing the browser will end the session and delete the storage.
It's up to you, which of the both you want to use.
For more, check out this SO question.
Cookies
Cookies are primarily for reading server-side, local storage can only be read by the client-side.
If I understand you correctly, you only want to save some UI state rather than have the server/backend remember this state.
In comparison to html5 storage, cookies are:
smaller
have an expiration date
are harder to implement (as it requires a backend)
For more, check out this SO question.
Edit: Cookies can actually be set and read by js alone, thanks for correcting me #jon-p

Javascript interval not clearing

I have a fairly complex page with lots of ajax calls and subsequent dom manipulation. On one particular call it sets an interval with a time limit(essentially a timer). I have set up clearInterval() everywhere and even in the function but in very particular use cases(it is complex and I can't determine the exact cause and steps to reproduce the defect) .
$(function() {
window.timer_interval;
// ...
})
function timer()
{
var current = 0;
time_limit = 60;
window.timer_interval = setInterval(function() {
minute = ( "0" + Math.round(Math.floor((time_limit - current)/60))).slice(-2);
seconds = ("0" + ((time_limit - current)%60)).slice(-2);
$('#timer').html(minute + ":" + seconds);
if (current >= time_limit) {
clearInterval(window.timer_interval);
window.timer_interval = false;
}
current = current + 1;
}, 1000);
}
I have used firbug to detect the value for window.timer_interval , it is false and even the condition is satisfied.One thing might be that that a couple of image transfers fail
(this is possible application behaviour with code written to gracefully degrade). I am developing in Mozilla.
My guess is that you're setting the interval, and then setting the interval again without clearing it first, so that whatever was set previous will run forever.
If I am right adding a check to clear the interval before setInterval will correct the problem. I've created a function to the code below that will take place when you call setInterval.
// starts an interval function, making sure to cancel one that is previously running
function startSharedInterval(func) {
if (window.timer_interval) {
clearInterval(window.intervalID);
window.timer_interval = 0;
}
window.timer_interval = setInterval(func, 1000);
};
// ... in timer()
startSharedInterval(function () {
minute = ( "0" + Math.round(Math.floor((time_limit - current)/60))).slice(-2) ;
// ... rest of code
});
If you only have one timer, then you can avoid using global scope and take advantage of using closures so that the timer is always cleared.
In the code below, interval_id is created in the parent timer() function. This will be available to the inner anonymous function to clear when its execution after 60 is completed. You may have multiple instances running simultaneously this way.
function timer() {
var current = 0;
var time_limit = 60;
var interval_id = setInterval(function () {
minute = ("0" + Math.round(Math.floor((time_limit - current) / 60))).slice(-2);
seconds = ("0" + ((time_limit - current) % 60)).slice(-2);
$('#timer').html(minute + ":" + seconds);
if (current >= time_limit) {
clearInterval(interval_id);
}
current = current + 1;
}, 1000);
}

Javascript: Taking the contents of a page element and using it as a variable?

So I have two scripts on a page:
The first one randomly chooses a number from an array and inserts it into an element called 'number'.
The second one is a countdown script which counts down from two variables which are interpreted as minutes and seconds. It then plays a sound once the countdown has reached zero.
At the moment, the second script is all manually set-up, so the number of minutes and seconds are hard-coded into the script itself. I'd like the second script to take whatever number was inserted into the page by the first script, and use it as its variable.
Here's a better example of how I'd like it to work:
You load the page, and it tells you that you only have (X) minutes to spare (X = inserted by first script), and whatever (X) is, acts as the 'minutes' variable in the second script, which is now displaying a countdown from (X) minutes elsewhere on the page.
The first script's output is inserted into:
<span id="minutes"></span>
And here's the code for the second script (the one that needs changing):
var interval;
var minutes = 5;
var seconds = 0;
window.onload = function() {
countdown('countdown');
}
function play(file) {
var embed = document.createElement("embed");
embed.setAttribute('src', file);
embed.setAttribute('hidden', true);
embed.setAttribute('autostart', true);
document.body.appendChild(embed);
}
function countdown(element) {
interval = setInterval(function() {
var el = document.getElementById(element);
if(seconds == 0) {
if(minutes == 0) {
el.innerHTML = "Time's up!";
clearInterval(interval);
play('alarm.mp3');
return;
} else {
minutes--;
seconds = 60;
}
}
if(minutes > 0) {
var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
seconds--;
}, 1000);
}
I've tried changing
var minutes = 5;
var seconds = 0;
to
var minutes = document.getElementById(minutes);
var seconds = 0;
(the second script runs after the first one)
... but it doesn't seem to work, just starting at 'null' minutes.
Could anyone shed any light on this?
Solution:
var minutes = Number(document.getElementById('minutes').innerHTML);
Explanation:
You probably mean the string minutes: (add quotes)
var minutes = document.getElementById('minutes');
var seconds = 0;
But that returns the DOM object. The innerHTML will return what is inside the <span> tag
var minutes = document.getElementById('minutes').innerHTML;
But then you need to realise that it is a string everywhere it is used, so that where you might have had implicit type conversions with string + number you will now have to do it explicitly, or change code to allow for implicit conversion, or handle it when you first instantiate minutes.
Thus we do the type conversion using Number()

My first jQuery plugin. Need help formatting and understanding how it works

I wrote my first plugin today: a simple tool to make the number in an element count upwards.
It works great, but I built it following examples and some trial and error so I can't say I understand how it works fully.
I don't understand:
a) How I should include handy functions like the secondsToTime() function (supposing I need it to be in a function - I understand that in this example it does not.) Why does it work here from within this.each block?
b) How are the variables I declared (_this, seconds, interval) scoped? They are all kept simultaneously for each element.
c) Could this plugin be structured better?
Code:
$(document).ready(function(){
$('.ticker').countup();
});
(function($) {
$.fn.countup = function() {
return this.each(function(){
var _this = this,
seconds = parseInt($(this).text()),
interval = setInterval(updateTicker, 1000 );
updateTicker();
function updateTicker(){
seconds += 1;
time = secondsToTime(seconds);
outputtime = time.h + ":" + ((time.m <= 9) ? '0' + time.m : time.m) + ":" + ((time.s <= 9) ? '0' + time.s : time.s)
$(_this).text(outputtime);
}
function secondsToTime(secs){
var hours = Math.floor(secs / (60 * 60));
var divisor_for_minutes = secs % (60 * 60);
var minutes = Math.floor(divisor_for_minutes / 60);
var divisor_for_seconds = divisor_for_minutes % 60;
var seconds = Math.ceil(divisor_for_seconds);
var obj = {
"h": hours,
"m": minutes,
"s": seconds
};
return obj;
}
});
};
})(jQuery);
Thanks for your feedback.
a) How I should include handy functions like the secondsToTime() function
I would move it out a level to within your (function($) { ... })(jQuery); function, since it doesn't have to be re-created for each element.
a) ...Why does it work here from within this.each block?
Because a function can be accessed from code defined at the same level of scope, or from a nested scope.
b) How are the variables I declared (_this, seconds, interval) scoped?
They're all specific to each call to the function you're passing into this.each. More specifically: When a function is called, an execution context for the call is created. That execution context has a variable object that contains the variables, function arguments, and functions declared within the function that was called (all of which are specific to the function call and so are created each time). Your updateTicker function (which is created for every call) is a closure over that variable object and so has an enduring reference to those variables. (More: Closures are not complicated.)
c) Could this plugin be structured better?
See (a) above.
I'd probably make my plug-in function a named function rather than an anonymous one. (More: Anonymouses anonymous) You already have your wrapper function (the one I mentioned in (a) above), so it would cost you nothing, but it makes debugging easier when functions actually have names.
I'd probably create the jQuery object for this just once and then reuse it, rather than doing it twice at the outset and then again each time updateTicker runs. E.g., make var _this = this, => var _this = $(this), and use _this.text on the next line and within updateTicker.
It's usually a good idea to force the radix on parseInt by supplying the second parameter (otherwise, weird things can happen with leading zeroes).
You might consider using just one interval timer to update all of the elements, rather than an interval for each element.
I'd add a way to stop the update.
Be aware that timers are not precise at all, so your countdowns may drift. You might consider grabbing the starting time and calculating how long it's actually been, rather than decrementing the seconds value.
Here's a first pass implementing just #1 - #4 above, leaving the others for you to do:
(function($) {
$.fn.countup = MyNiftyPlugin_countup; // #2
function MyNiftyPlugin_countup() { // #2 cont'd
return this.each(function(){
var _this = $(this), // #3
seconds = parseInt(_this.text(), 10), // #3 cont'd, #4
interval = setInterval(updateTicker, 1000 );
updateTicker();
function updateTicker(){
seconds += 1;
time = secondsToTime(seconds);
outputtime = time.h + ":" + ((time.m <= 9) ? '0' + time.m : time.m) + ":" + ((time.s <= 9) ? '0' + time.s : time.s)
_this.text(outputtime); // #3 cont'd
}
});
}
function secondsToTime(secs){ // #1 (moving this out a level)
var hours = Math.floor(secs / (60 * 60));
var divisor_for_minutes = secs % (60 * 60);
var minutes = Math.floor(divisor_for_minutes / 60);
var divisor_for_seconds = divisor_for_minutes % 60;
var seconds = Math.ceil(divisor_for_seconds);
var obj = {
"h": hours,
"m": minutes,
"s": seconds
};
return obj;
}
})(jQuery);
Improving on the code sample #T.JCrowder gave.
Here were taking the interval outside the loop and only running it once. We're storing the time as an integer in the data so we only have to parseInt once. Were getting the toTime function to return the formatted string. and a couple of other minor improvements.
(function($) {
$.fn.countup = MyNiftyPlugin_countup;
function MyNiftyPlugin_countup() {
var that = this;
function updateTicker(){
that.each(updateNode);
}
function updateNode() {
var $this = $(this); // cache
var seconds = $this.data("time") + 1; // get time from $.data
$this.data("time", seconds);
//var seconds = Date.now() // alternative get accurate time right _now_
var time = secondsToTime(seconds)[1]; // get string from tuple
$this.text(time).data("time", seconds);
}
setInterval(updateTicker, 1000);
updateTicker();
return this.each(function(){
var $this = $(this); // cache
$this.data("time", parseInt($this.text(), 10);
});
}
function secondsToTime(secs){
var hours = Math.floor(secs / (60 * 60));
var divisor_for_minutes = secs % (60 * 60);
var minutes = Math.floor(divisor_for_minutes / 60);
var divisor_for_seconds = divisor_for_minutes % 60;
var seconds = Math.ceil(divisor_for_seconds);
var time = {
"h": hours,
"m": minutes,
"s": seconds
};
var outputtime = time.h + ":" + ((time.m <= 9) ? '0' + time.m : time.m) + ":" + ((time.s <= 9) ? '0' + time.s : time.s)
return [time, outputtime];
}
})(jQuery);

Categories

Resources