I'm writing a script that lets a user know how much time he spent on a page before refreshing it. To this purpose, I increment a time counter with a setInterval function, and store the data in the browser thanks to localStorage. Once the page is refreshed, I retrieve the data stored, and display them. Meanwhile, the time counter goes back to 0 and starts incrementing again.
Unfortunately, something is wrong with my script because localStorage doesn't store the updated time value (it's always -1). What's wrong with my script?
//TimeSpent = -1, so setInterval sets it to 0s instead of 1s when the page opens.
var timeSpent = -1
//Update time every second
var timer = setInterval(()=>{
timeSpent +=1;
}, 1000);
//If first visit, ask to refresh. Else, display timeSpent on previous page by retrieving localStorage data.
function start(){
if (localStorage.timeData){
var timerJson = localStorage.getItem("timeData");
var timerParsed = JSON.parse(timerJson);
console.log(`You spent ${timerParsed.time} seconds on previous page`)
}
else{
console.log("Reload the page and see how much time you spent reading this.")
}
}
//Trig function when page opens.
window.onload = start();
//Before page reloads, store timeSpent in localStorage as a Json file.
var timeData = {
time: timeSpent,
}
function storeData (timeData){
var timerJson = JSON.stringify(timeData)
localStorage.setItem("timeData", timerJson);
}
window.onbeforeunload = storeData (timeData)
Thanks!
window.onbeforeunload must have a value of type function but in your code it is undefined. Hence you should change it to this:
window.onbeforeunload = function storeData (){
var timerJson = JSON.stringify(timeData)
localStorage.setItem("timeData", timerJson);
}
I've also removed the parameter from the function, making it a closure.
UPD. As Jonas Wilms noted, you should do the same wilth onload event and start function.
ALSO. In order to always have the actual (fresh) value of timeSpent, you should do this:
const state = {timeSpent: -1}
And everywhere replace timeSpent with state.timeSpent.
This way the closures will have a link to state object, instead of just taking the initial value of a primitive timeSpent.
This code works well for me:
let timeData = {
time: -1
}
timeData.time = setInterval(()=>{
timeData.time += 1
console.log(timeData.time)
}, 1000);
function start(){
if (localStorage.timeData){
var timerJson = localStorage.getItem("timeData");
var timerParsed = JSON.parse(timerJson);
console.log(`You spent ${timerParsed.time} seconds on previous page`)
}
else{
console.log("Reload the page and see how much time you spent reading this.")
}
}
window.onload = start();
window.onbeforeunload = function () {
var timerJson = JSON.stringify(timeData)
localStorage.setItem("timeData", timerJson);
}
I assume you testing this locally. Since local-storage is stored like a cookie domain based (and you dont have a domain when you test your script locally) the data is simply not saved.
In HTML5, is the localStorage object isolated per page/domain?
Edit: By local i mean a simple Html-File without using a webserver.
Related
I would like to have a countdown timer always show a countdown for every new user. Basically, if I close the webpage, and reopen it, the timer should still be running. I'm thinking of using the JS variable code functions to define a new client's timezone together with an if statement comment and make it a repeat loop?
Basically, I would want to run a timer on the server side, not the client side.
Has anyone done this before?
Sounds something that you could try to solve with browsers localStorage. If your only requirement is to keep saved time available after you close tab/browser and come back to your page, this is a good fit for you.
Here's a small Codesandbox example code of how to conditionally check and save to localStorage and then start counter based on that value.
https://codesandbox.io/s/4xw97q02m0
EDIT: same code pasted to a post
function setCurrentTime(){
let localStore = window.localStorage.getItem("myTime") // Check if already exists
if(localStore) return localStore
const time = Date.now(); // get current time stamp
window.localStorage.setItem("myTime", time) // Save to localStorage
return time
}
function startCount(){
const time = setCurrentTime()
const elem = document.getElementById("app")
setInterval(() => {
elem.innerHTML = `seconds since time saved:
${Math.floor((Date.now() - time) / 1000)}
`
}, 1000)
}
startCount()
<div id="app"></div>
I have a date input in my page, which I'm using Daterangepicker framework to populate it.
Here is the code of how I start my page!
$(function(){
startSelectors();
var variaveis = returnInputVars();
var rede = variaveis[0];
var codLoja = variaveis[1];
var period = variaveis[2];
console.log('1.'+rede+' 2.'+codLoja+' 3.'+period);
});
function returnInputVars(){
var rede = $("#dropdown-parceria").val();
var codLoja = $("#dropdown-loja").val();
var periodo = $("#datepicker-range").val();
return [rede, codLoja, periodo];
};
The function startSelectors() is set to start my datepicker and other fields, which is working perfectly. After it, I create a var called "variaveis" to fill
with the values of each field because I will use then later (this functions also works perfectly at other scripts of my page).
Running the page, my console returns this:
The funny thing is, if I type at the console this, the value is shown, just while starting the script is does not work!
Anybody experienced something like this?
***UPDATE
Adding this script to my start function:
console.log($("#datepicker-range"));
The value is shown, but the second console.log don't:
EDIT 1. FIDDLE (Suggested by #halleron)
To ensure things are loaded in the correct order, it is useful to apply a page sniffer code snippet that will scan the page continuously until a condition is met, or until a preset counter limit is reached (to prevent strain on browser memory). Below is an example of what I typically use that would fit your scenario.
I think because you are dealing with asynchronous loading, you can't have a global variable that holds the values in a global scope without an interval to detect when it can be used. Otherwise, it will attempt to read the variable when it is not yet ready.
You can invoke functions anywhere you like. But I would keep all of your variables contained within the page_sniffer_2017() because that is a controlled environment where you know that everything successfully loaded and you know that the variables are ready to be accessed without error.
That way, regardless of connection speed, your functions will only fire when ready and your code will flow, sequentially, in the right order.
Within the ajax success options, always add a class to the body of the document that you can search on to determine if it has finished loading.
$(document).ready(function() {
page_sniffer_2017();
});
function page_sniffer_2017() {
var counter = 0;
var imgScanner = setInterval(function() {
if ($("#datepicker-range").length > 0 && $("#datepicker-range").val().length && jQuery('body').hasClass('date-picker-successfully-generated')) {
var periodoDatepicker = $("#datepicker-range").val(); // ok
console.log(periodoDatepicker); // ok
var variaveis = returnInputVars(replaceDate(periodoDatepicker)); // ok
console.log(variaveis[0], variaveis[1], variaveis[2]);
//startNewSelectors(variaveis);
// start ajax call
generateData(variaveis[0], variaveis[1], variaveis[2]);
clearInterval(imgScanner);
} else {
//var doNothing = "";
counter++;
if (counter === 100) {
console.log(counter);
clearInterval(imgScanner);
}
}
}, 50);
}
I have this javascript code where the setInterval() is triggered every 2 sec to update the var kraken_btc_eur
However sometimes the variable retrieved from the API does not change. Therefore, to save some serveur processing I would like to avoid the setInterval action to be triggered .
Maybe what I am asking does not make sense, it is just a though for optimisation.
Thank you for your guidance.
var kraken_btc_eur_old = 0;
setInterval(function(){
$.get('https://api.kraken.com/0/public/Ticker?pair=XXBTZEUR', function(data){
var kraken_btc_eur = data.result.XXBTZEUR.c[0]; //get the value of 1 bitcoin
//some logic to change the css if the value increased or decreased
if (kraken_btc_eur_old > kraken_btc_eur) {
$(".kraken_btc_eur").css('color', 'red').text(kraken_btc_eur);
} else {
$(".kraken_btc_eur").css('color', 'green').text(kraken_btc_eur);
}
kraken_btc_eur_old = kraken_btc_eur; //set the global variable to the value of 1 bitcoin so that in 2 sec it will be checked in the if statement
$(".kraken_btc_eur").text(kraken_btc_eur); //show the value to the user to the html tag with the corresponding class
});
}, 2000);
With setInterval, you are using an approach called short-polling. This is when you continuously request data from the server to determine if anything changed.
There are two major alternatives to short-polling. Of these, I believe you are looking for WebSockets, which are essentially Sockets that you can use with JavaScript. WebSockets allow you to pass unformatted data from the client to the server, and vice versa. Using WebSockets, your server would have to keep an open socket to the client, but would only send data to the client if something changed on the server's side.
Of course, this is assuming you are the developer of the API.
If not, you'll have to stick to short polling. You could have an approach where if the value from the API doesn't change for a while, you could decrease the frequency of your short polls - but that would require you to switch from setInterval to setTimeout. In your callback, you could determine what the new timeout will be between the current callback and the next callback.
This approach would look something like the following:
setTimeout(function callback(timeout){
// .. get request here
if(timeout !== undefined)
{
if(kraken_btc_eur == kraken_btc_eur_old)
{
timeout = Math.min(10000, timeout + 1000);
}
else
{
timeout = 2000;
}
}
else
{
timeout = 2000;
}
setTimeout(callback.bind(window, timeout), timeout);
}, 2000);
I believe kraken api has an etag header not so sure. You can check if the etag is the same with the old etag means the data hasn't change.
Some reference.
http://www.websiteoptimization.com/speed/tweak/etags-revisited/
https://www.infoq.com/articles/etags
If you have the option of setting up a websocket its better. Check https://socket.io/.
You can make few changes in your code
var window.kraken_btc_eur_old = 0;
function makeRequest(oldValue) {
$.get('https://api.kraken.com/0/public/Ticker?pair=XXBTZEUR',
function(data){
var kraken_btc_eur = data.result.XXBTZEUR.c[0];
if(kraken_btc_eur !== oldValue){
//get the value of 1 bitcoin
//some logic to change the css if the value increased or decreased
if (oldValue > kraken_btc_eur) {
$(".kraken_btc_eur").css('color', 'red').text(kraken_btc_eur);
} else {
$(".kraken_btc_eur").css('color', 'green').text(kraken_btc_eur);
}
window.kraken_btc_eur_old = kraken_btc_eur;
//set the global variable to the value of 1 bitcoin so that in 2 sec it will be checked in the if statement
$(".kraken_btc_eur").text(kraken_btc_eur); //show the value to the user to the html tag with the corresponding class
}
});
}
setInterval(makeRequest.bind(null,window.kraken_btc_eur_old), 2000);
I have written window.kraken_btc_eur_old just to give you an understanding that it's a global object
I have a table of data for the user to look through and make changes. They have the option to delete a row if they want. When they delete a row it sends back to the db what row is to be deleted. For the user the row won't delete right away but it will get a new class 'deleted' that has some styles to let the user know it's being deleted. I also have the page refreshing every 10 minutes and when it refreshes the new added 'deleted' class goes away.
How can I get it that the class will stay on the row when the page refreshes?
Here is the timer function for the timer to know when to refresh the page. I'm using ajax to refresh the page.
function startClock() {
// Get the time to stop the effect
var stopTime = new Date();
stopTime.setMinutes(stopTime.getMinutes() + 10);
// Get a reference to the timer so it can be cancelled later
timer = setInterval(function () {
// Check to see if the timer should stop
var currentTime = new Date();
if (currentTime < stopTime) {
triggerDataTable(); //refresh dataTable
var randomnumber = Math.floor(Math.random() * 100);
} else {
// Stop the timer
clearInterval(timer);
}
}, 600000);
}
Would I use localStorage? If so how would I actually do it? I'm not familiar with storing local values or anything like that.
I'm not 100% sure about this but what about doing something like this:
var deletedRow = $(".deleted");
localStorage.setItem( deletedRow, $(".deleted").val() );
try using sessionstorage over localstorage
Session Storage property maintains a separate storage area for each given origin and for a session .
Local Storage does the same thing, but persists even when the browser is closed and reopened.
sessionStorage.setItem('item', value);
sessionStorage.getItem('item');
I want to keep track of how many seconds a user spend on my project website on all of his pages and for doing this I want to use JavaScript session cookies.
This is because I will host it using Github Pages where I have no access to PHP and also because I want to try working with JS cookies (It's the first time I am doing this).
To count the seconds I am using the followings:
var time_spent = 0;
setInterval(function(){
++time_spent;
$('#counter_container span').html(time_spent);
}, 1000);
The above code works where the counter is but as expected is reseting every time I change the page.
I've tried using a js-cookie library (https://github.com/js-cookie/js-cookie) in the following manner:
Cookies.set('time_spent', time_spent);
var counted_time = Cookies.get('time_spent');
console.log(counted_time);
But the counted_time variable is always 'undefined' whatever I've tried.
I would really apreciate any guidance I can get in order to solve this little issue.
I wouldn't use a timer for this. Instead try setting a timestamp when the user enters the page, and then onbeforeunload get the duration and add it to the value stored in the cookie. Something like this:
var load = new Date();
window.onbeforeunload = function() {
var leave = new Date();
var duration = leave.getTime() - load.getTime() / 1000;
var counted_time = parseFloat(Cookies.get('time_spent')) || 0;
Cookies.set('time_spent', counted_time + duration);
}
Working example