How to make a stopwatch continue counting after closing the page?

Asked

Viewed 4,694 times

13

I need to create a stopwatch that is started through a play, but wanted a solution, other than by SESSION, to let you count even if the customer closes the window or drops his internet, ie that is not dependent on cache or front.

How to do this with PHP, Javascript and Mysql?

  • 1

    Keep counting until when? If the page reopens displays the same timer?

  • Exactly! It counts until a pause or stop! You can continue when you press pause and then play. However, you should never stop counting when you’re in 'play'.

  • I would save the timestamp at the time the play is pressed into the bank and would make a stopwatch on the front-end, If the user presses pause, calculates the time and saves it in another bank table. If the user closes the page and returns, check if there is already a "timer" for it in the seat and bring the initial value to the front timer.

  • That’s the problem: "... If the user closes the page and returns, check if there is already a "timer" for it in the seat and bring the initial value to the front timer."

  • 1

    If it’s any page where the user doesn’t always have a unique identifier, I don’t see how it can be done. Oh not be that the stopwatch is not individual, but rather something related to page, and just call an ajax on onLoad.

  • Bro, the user presses play and is started. The page will only be closed if: the internet drops, if it closes accidentally... But he should always open the page and see how far he is running his time from his play. And in the end it clicks on stop or pause to be able to stop. Maybe the thing is like you said, check the team and calculate between the beginning and the end, but if it is 3 days or more?

  • What’s the problem of being 3 days or more? Play = saved timestamp, Pause = timestamp_current - timestamp_saved and stored in a timer_total table. Play takes the value stored in the timer_total and uses it as the initial value of the stopwatch, Pause repeats the calculation, but now increments the record already saved from the timer_total.

  • 1

    You can give an example as an answer?

  • Do you need IE compatibility? If you don’t need it, you can use something based on localStorage

  • How do you want the customer to identify? I imagine you want to have a chronometer per user, there is some login?

  • @Sergio, it will be a stopwatch for each customer, but the focus of the question is on how to continue counting even after a computer shutdown until a cash wipe... Anyway! Help awe!

Show 6 more comments

6 answers

13


Considering that the stopwatch should store the current situation, I propose the following structure:

  • When accessing timer.html, an AJAX request to get_timer.php takes the value of the stopwatch and the situation (paused/running);
  • The "Start" and "Pause" buttons send an AJAX request to grava_acao.php that records the timestamp and the current action based on the last recorded action;
  • It will be possible to generate reports with the intervals stopped and running;

database

CREATE TABLE `timer` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `user_id` INT(10) UNSIGNED  NOT NULL,
    `action` VARCHAR(5) NOT NULL,
    `timestamp` INT(10) UNSIGNED NOT NULL,
    PRIMARY KEY(`id`)
);

timer.html

<script src="http://code.jquery.com/jquery.js"></script>
<script>
var format = function(seconds) {
    var tempos = {
        segundos: 60
    ,   minutos: 60
    ,   horas: 24
    ,   dias: ''
    };
    var parts = [], string = '', resto, dias;
    for (var unid in tempos) {
        if (typeof tempos[unid] === 'number') {
            resto = seconds % tempos[unid];
            seconds = (seconds - resto) / tempos[unid];
        } else {
            resto = seconds;
        }
        parts.unshift(resto);
    }
    dias = parts.shift();
    if (dias) {
        string = dias + (dias > 1 ? ' dias ' : ' dia ');
    }
    for (var i = 0; i < 3; i++) {
        parts[i] = ('0' + parts[i]).substr(-2);
    }
    string += parts.join(':');
    return string;
};
$(function(){
    var tempo = 0;
    var interval = 0;
    var timer = function(){
        $('.timer').html(format(++tempo));
    };
    $.post('get_timer.php', function(resp){
        $('button').text(resp.running ? 'Pausar' : 'Iniciar');
        tempo = resp.seconds;
        timer();
        if (resp.running) {
            interval = setInterval(timer, 1000);
        }
    });
    $('button').on('click', function(){
        var btn = this;
        btn.disabled = true;
        $.post('grava_acao.php', function(resp){
            btn.disabled = false;
            $(btn).text(resp.running ? 'Pausar' : 'Iniciar');
            if (resp.running) {
                timer();
                interval = setInterval(timer, 1000);
            } else {
                clearInterval(interval);
            }
        });
    });
});
</script>
<div class="timer">0 segundos</div>
<button>Iniciar</button>

get_timer.php

<?php
// definir no seu código
$pdo = new PDO($dsn, $username, $passwd);
$user_id = 1; // $_SESSION['user_id'];

$params = array(':user' => $user_id);
$stt = $pdo->prepare('SELECT `action`, `timestamp` FROM `timer` WHERE `user_id` = :user ORDER BY `id`');
$stt->execute($params);
$tempos = $stt->fetchAll(PDO::FETCH_ASSOC);
$seconds = 0;
$action = 'pause'; // sempre inicia pausado
foreach ($tempos as $tempo) {
    $action = $tempo['action'];
    switch ($action) {
        case 'start':
            $seconds -= $tempo['timestamp'];
            break;
        case 'pause':
            // para evitar erro se a primeira ação for pause
            if ($seconds !== 0) {
                $seconds += $tempo['timestamp'];
            }
            break;
    }
}
if ($action === 'start') {
    $seconds += time();
}
header('Content-Type: application/json');
echo json_encode(array(
    'seconds' => $seconds,
    'running' => $action === 'start',
));

grava_acao.php

<?php
// definir no seu código
$pdo = new PDO($dsn, $username, $passwd);
$user_id = 1; // $_SESSION['user_id'];

$params = array(':user' => $user_id);
$stt1 = $pdo->prepare('SELECT `action` FROM `timer` WHERE `user_id` = :user ORDER BY `id` DESC LIMIT 1');
$stt1->execute($params);
$newAction = 'start';
if ($stt1->rowCount() && $stt1->fetchColumn() === 'start') {
    $newAction = 'pause';
}
$stt2 = $pdo->prepare('INSERT INTO `timer` (`user_id`, `action`, `timestamp`) VALUES (:user, :action, :time)');
$params[':action'] = $newAction;
$params[':time'] = time();
$stt2->execute($params);
header('Content-Type: application/json');
echo json_encode(array(
   'running' => $newAction === 'start',
)); // para atualizar status, no caso de concorrência
  • This is the way! One must create a routine to evaluate the situation paused/running, to then generate a mathematical calculation that will bring the time of the interval between the beginning and the return to the screen!

  • Two things: It will always be a counting up and when I open the page, if I come back from a possible fall/close, the timer should start/start automatically! But you’ve come to the right path so far!

  • PERFECT! NO GLITCH OR COMPLICATION!

  • Sanction, last request: could set to 00:00:00. At the moment is 1 minutes 50 Seconds. THANK YOU!

  • Sanction, when it passes 6 days of play it will stay 144 hours. This first house of 00:00:00, will count 144:00:00?

  • 1

    when there are more than 24h already changes to days: 6 dias 23:59:58 and 7 dias 00:00:38, when has less does not appear as condition if (dias)

  • SHOW!!!!!!!!!!!

Show 2 more comments

12

Can implement a stopwatch front-end using Javascript where by clicking on Play starts the timer, when the user clicks Pause or close the window should then send a Ajax to the PHP update the current value of the timer in the database. The next time the screen opens, then a request is made Ajax at the PHP to return the current value of the timer that is saved in the bank.

HTML:

<body onload="restore_timer();">
    <form name="form">
        <input type="text" name="cronometro" value="00:00:00" />
        <br />
        <button type="button" onclick="timer(this); return false;">Play</button>
    </form>
</body>

Javascript:

$(document).ready(function() {
    var t = form.cronometro.value;
    var hora = t.substring(0, 2);
    var minuto = t.substring(3, 5);
    var segundo = t.substring(6, 8);

    restore_timer = function() {
        send_ajax("get_time");
    }

    var interval;
    send_ajax = function(opt) {
        $.ajax({
            method: "POST",
            async: "false",
            url: "timer.php",
            data: {"opt": opt}
        }).done(function(msg) {
            if (opt == "get_time") {
                if (msg.status == "started") {
                    $('button').html('Pause');
                    interval = setInterval('tempo()', 983);
                }
                form.cronometro.value = msg.time;
                t = msg.time;
                hora = t.substring(0, 2);
                minuto = t.substring(3, 5);
                segundo = t.substring(6, 8);
            }
        });
    }

    timer = function(obj) {
        var html = $(obj).html();
        if (html == 'Play') {
            interval = setInterval('tempo()', 983);
            send_ajax("start_timer");
            $(obj).html('Pause');
        } else {
            clearInterval(interval);
            send_ajax("save_time");
            $(obj).html('Play');
        }
    }

    tempo = function(){ 
       if (segundo < 59) {
          segundo++;
          if (segundo < 10)
             segundo = "0" + segundo;
       } else if (segundo == 59 && minuto < 59) {
          segundo = 0 + "0";
          minuto++;
          if (minuto < 10)
             minuto = "0" + minuto;
       }
       if (minuto == 59 && segundo == 59) {
          segundo = 0 + "0";
          minuto = 0 + "0";
          hora++;
          if (hora < 10)
             hora = "0" + hora;
       }
       form.cronometro.value = hora + ":" + minuto + ":" + segundo;
    }
});

PHP:

if ($_POST) {
    $opt = $_POST['opt'];

    $servername = "localhost";
    $username = "USUARIO";
    $password = "SENHA";
    $dbname = "BANCO_DE_DADOS";

    $conn = new mysqli($servername, $username, $password, $dbname);
    if ($conn->connect_error) {
        die("Connection failed: " . $conn->connect_error);
    }

    if ($opt == "get_time") {
        $sql = "SELECT * FROM time_stamps WHERE id = 1";
        $result = $conn->query($sql);

        $old_time_stamp;
        header('Content-Type: application/json');
        if ($result->num_rows > 0) {
            while($row = $result->fetch_assoc()) {
                $old_time_stamp = $row["time_stamp"];
            }

            $date = new DateTime();
            $time_stamp = $date->getTimestamp();

            echo json_encode(array("status" => "started", "time" => date('H:i:s', ($time_stamp - $old_time_stamp))));
        } else {
            $sql = "SELECT * FROM timer WHERE id = 1";
            $result = $conn->query($sql);

            if ($result->num_rows > 0) {
                while($row = $result->fetch_assoc()) {
                    echo json_encode(array("time" => $row["time"]));
                }
            }
        }
    } elseif ($opt == "start_timer") {
        $date = new DateTime();
        $time_stamp = $date->getTimestamp();
        $sql = "INSERT INTO time_stamps VALUES (1, '{$time_stamp}')";
        $result = $conn->query($sql);
    } elseif ($opt == "save_time") {
        $sql = "SELECT * FROM time_stamps WHERE id = 1";
        $result = $conn->query($sql);

        $old_time_stamp;
        if ($result->num_rows > 0) {
            while($row = $result->fetch_assoc()) {
                $old_time_stamp = $row["time_stamp"];
            }
        }

        $sql = "DELETE FROM time_stamps WHERE id = 1";
        $result = $conn->query($sql);

        $date = new DateTime();
        $time_stamp = $date->getTimestamp();

        $new_time = explode(":", date('H:i:s', ($time_stamp - $old_time_stamp)));

        $sql = "SELECT * FROM timer WHERE id = 1";
        $result = $conn->query($sql);

        $old_time;
        if ($result->num_rows > 0) {
            while($row = $result->fetch_assoc()) {
                $old_time = explode(":", $row["time"]);
            }
        }

        $hour = intval($old_time[0] + $new_time[0]);
        $minute = intval($old_time[1] + $new_time[1]);
        $second = intval($old_time[2] + $new_time[2]);
        while ($second >= 60) {
            $second = $second - 60;
            $minute++;
        }
        while ($minute >= 60) {
            $minute = $minute - 60;
            $hour++;
        }
        if ($second < 10)
            $second = "0" . strval($second);
        if ($minute < 10)
            $minute = "0" . strval($minute);
        if ($hour < 10)
            $hour = "0" . strval($hour);

        $time = "{$hour}:{$minute}:{$second}";

        $sql = "UPDATE timer SET time = '{$time}' WHERE id = 1";
        $result = $conn->query($sql);
    }
    $conn->close();
}

Database:

CREATE TABLE timer (
    id INT PRIMARY KEY,
    time VARCHAR(10) NOT NULL
);

CREATE TABLE time_stamps (
    id INT PRIMARY KEY,
    time_stamp VARCHAR(255) NOT NULL
);

INSERT INTO timer values (1, "00:00:00");

In this example I have only one record in the table timer, if you need more will need to change the logic a little, but I believe this won’t be a problem.

Follows working example.

Heed: I did not validate the value transited by the requests, if the user changes the HTML in hand and give Pause will send to the bank and save normally.

  • But the point is LET GO AND NOT PAUSE AND CONTINUE FROM WHERE YOU CLOSED THE WINDOW.

  • Bro, thank you so much for the strength, but it’s still not that. It’s almost that.

  • @Lollipop so I had suggested timestamp and did not remember, when I went to make the example now I thought "so it is simpler, do not need the timestamp"... I will correct and shortly edit the answer.

  • 1

    Here is a situation: I started the work at 13:00, closed the window accidentally or turned off the PC at 13:30, 'and when I come back', should be calculated the time I started until the time I returned, continuing the count automatically from 13:30... I think the great pitch is to differentiate the pause from the "running", if I did not pause and have a difference, it is pq ta rotating! If there is pause, continue from pause, as you did. But the boxwood will be pick up after a pause and play! CAN UPDATE THE TBM EXAMPLE? Hug!

  • @Lollipop updated the answer and the example working, I had to leave here and ended up taking a little while.

  • Bro, it seems to work! I’m going to test it here and I’m coming back to give an award! :)

  • PHP can still be refactored, I was writing as I thought and repeated several things, but the idea is this.

  • Luis Henrique, not working! Uncaught Typeerror: Cannot read Property 'substring' of Undefined. NO JS!

  • The variable t is null, was that inside Ajax? Puts a console.log(msg) and see if PHP returned right.

  • It worked, but the timer table ta picking up the hours like this: 70:01:08. This may be a problem neh?

  • Depends what’s the problem for you. The timer table is only used to store the time spent and return to javascript, in the calculation it is always used timestamp. Just look at the size of the field in the table timer that if the time turns 100 will burst I think.

  • Luis Henrique, why is the code of your example different from the code posted here? It’s not working from where it left off.

  • @Lollipop The table name is time_stamps. Corrected.

  • It’s giving a hell of a bug! When I update the page, it goes to 18:00:00

  • Even your example, when I start doing everything right, but when I pause and give play Uga tbm. Ve awe mano!

  • @Lollipop dropped the server cPanel here, I won’t be able to fix it. But basically you have to do the following: no if($opt == "get_time") where it checks if there is a running timestamp (is the first if) you have to select in the timer table and add the value, equal is done in the save_time down there. Just copy and paste things. Copy from $new_time = explode... until $time = "{$hour}..."

Show 11 more comments

4

I have developed a solution with persistent data stored on file and using localStorage. I believe this solution will suit your case.

The solution is simple, the code is great because I created a manager to test the operation.

If you want to store a database, simply change the persistence functions of the class.

<?php

//
// Lib 
//

const CHRON_PATH  = '\path\to\data\\';

const CHRON_STOP  = 0x00;
const CHRON_PAUSE = 0x01;
const CHRON_PLAY  = 0x03;

class PersistentChronometer {
    // fields

    private $_id;
    private $_elapsed;
    private $_status;
    private $_statustime;

    // construtor privado

    private function __construct() {}

    // propriedades

    function __get($prop) {
        switch($prop) {
        case 'id':
            return $this->_id;
        case 'status':
            return $this->_status;
        case 'elapsed':
            $elapsed = $this->_elapsed;

            if ($this->_status == CHRON_PLAY)
                $elapsed += max(microtime(true) - $this->_statustime, 0);

            return $elapsed;
        }
    }

    // comandos

    public function play() {
        if($this->_status == CHRON_PLAY)
            return;

        $this->_statustime = microtime(true);
        $this->_status = CHRON_PLAY;
    }

    public function pause() {
        if($this->_status != CHRON_PLAY)
            return;

        $time = microtime(true);

        $this->_elapsed += max($time - $this->_statustime, 0);

        $this->_statustime = microtime(true);
        $this->_status = CHRON_PAUSE;
    }

    public function stop() {
        if($this->_status == CHRON_STOP)
            return;

        $this->_statustime = microtime(true);
        $this->_elapsed = 0;
        $this->_status = CHRON_STOP;
    }

    // persistência

    public static function create() {
        $chron = new PersistentChronometer();
        $chron->_statustime = microtime(true);
        $chron->_elapsed = 0;
        $chron->_status = CHRON_STOP;

        $i = 0;
        while (true) {
            $chron->_id = ((int)$chron->_statustime) . '$' . $i++;

            $fname = CHRON_PATH . $chron->_id . '.chron';
            if(file_exists($fname)) 
                continue;
            $f = fopen($fname, 'w');
            fwrite($f, $chron->serialize());
            fclose($f);
            break;
        } 

        return $chron;
    }

    public static function load($id) {
        $fname = CHRON_PATH . $id . '.chron';

        if(!file_exists($fname))
            return false;
        $f = fopen($fname, 'r');
        $chron = PersistentChronometer::unserialize(fread($f, 255));
        fclose($f);

        return $chron;
    }

    public function save() {
        $fname = CHRON_PATH . $this->_id . '.chron';
        $f = fopen($fname, 'w');
        ftruncate($f, 0);
        fwrite($f, $this->serialize());
        fclose($f);
    }

    public function delete() {
        $fname = CHRON_PATH . $this->_id . '.chron';
        unlink($fname);
    }

    public function serialize() {
        return json_encode(array(
            'id' => $this->_id,
            'elapsed' => $this->_elapsed,
            'status' => $this->_status,
            'statustime' => $this->_statustime
        ));
    }

    public static function unserialize($string) {
        $data = json_decode($string);

        $chron = new PersistentChronometer();
        $chron->_id = $data->id;
        $chron->_elapsed = $data->elapsed;
        $chron->_status = $data->status;
        $chron->_statustime = $data->statustime;

        return $chron;
    }
}

//
// Comandos
//

if(isset($_GET['action'])) {
    switch($_GET['action']) {
    case 'play':
        if(isset($_GET['id'])
          && (($chron = PersistentChronometer::load($_GET['id'])) !== false)) {
            $chron->play();
            $chron->save();
        }
        break;
    case 'pause':
        if(isset($_GET['id'])
          && (($chron = PersistentChronometer::load($_GET['id'])) !== false)) {
            $chron->pause();
            $chron->save();
        }
        break;
    case 'stop':
        if(isset($_GET['id'])
          && (($chron = PersistentChronometer::load($_GET['id'])) !== false)) {
            $chron->stop();
            $chron->save();
        }
        break;
    case 'new':
        PersistentChronometer::create();
        break;
    case 'delete':
        if(isset($_GET['id'])
          && (($chron = PersistentChronometer::load($_GET['id'])) !== false)) {
            $chron->delete();
        }
        break;
    case 'get':
        if(isset($_GET['id'])
          && (($chron = PersistentChronometer::load($_GET['id'])) !== false)) {

            $data = new stdClass();
            $data->id = $chron->id;
            $data->elapsed = $chron->elapsed;
            $data->status = $chron->status;
            $data->result = true;

            echo json_encode($data);
        } else {
            echo '{"result": false}';
        }

        return;
    }
}

//
// Output
//

?>
<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="//code.jquery.com/jquery-2.1.3.min.js"></script>
    <script>
        $(function(){
            var cEl = $('#chron');
            if(!window.localStorage)
                cEl.text('Para esta solução é necessário o uso de localStorage. Mas também podem ser utilizados cookies ou variaveis de sessão!!!');
            else {
                var id = localStorage.getItem("chron-id");

                if(!id)
                    cEl.text('Nenhum cronômetro definido!');
                else {
                    loadChronometer(id);
                    cEl.text('Aguarde...');
                }
            }
        });

        function setChronometer(id) {
            localStorage.setItem("chron-id", id);
            loadChronometer(id);
        }

        function loadChronometer(id) {
            var status = {
                <?php echo CHRON_STOP ?>: 'Parado',
                <?php echo CHRON_PLAY ?>: 'Executando',
                <?php echo CHRON_PAUSE ?>: 'Interrompido'
            };
            var cEl = $('#chron');
            $.getJSON('?action=get&id=' + id, null, function(data) {
                if(data.result)
                    cEl.text(
                        'ID: ' + data.id + "\n" +
                        'Estado: ' + status[data.status] + "\n" +
                        'Tempo (seg): ' + data.elapsed + "\n"
                    ).append(
                        '<a href="javascript:setChronometer(\'' + data.id + '\')">Atualizar</a> | ' +
                        (data.status == <?php echo CHRON_STOP ?> ? '' : '<a href="?action=stop&id=' + data.id + '">Parar</a> | ') +
                        (data.status == <?php echo CHRON_PLAY ?> ? '' : '<a href="?action=play&id=' + data.id + '">Executar</a> | ') +
                        (data.status != <?php echo CHRON_PLAY ?> ? '' : '<a href="?action=pause&id=' + data.id + '">Interromper</a> | ') +
                        '<a href="javascript:setChronometer(null)">Remover</a>' 
                    );
                else
                    cEl.text('Nenhum cronômetro definido!');
            });
        }
    </script>
</head>
<body>
<h1>Cronômetro padrão</h1>

<pre id="chron" style="white-space: pre"></pre>


<h1>Gerenciador</h1>
<table>
    <thead>
        <td>ID</td>
        <td>Estado</td>
        <td colspan="5">Comandos</td>
        <td>Tempo (seg)</td>
    </thead>
<?php

$files = scandir(CHRON_PATH);
$chrons = array();
foreach($files as $file) {
    if (substr($file, -6) == '.chron')
        $chrons[] = PersistentChronometer::load(substr($file, 0, -6));
}

$status = array(
    CHRON_STOP  => 'Parado',
    CHRON_PLAY  => 'Executando',
    CHRON_PAUSE => 'Interrompido'
);

foreach($chrons as $chron) {
    $td = array();
    $td[] = $chron->id;
    $td[] = $status[$chron->status];

    $td[] = $chron->status == CHRON_STOP ? '' : '<a href="?action=stop&id='. $chron->id .'">Parar</a>';
    $td[] = $chron->status == CHRON_PLAY ? '' : '<a href="?action=play&id='. $chron->id .'">Executar</a>';
    $td[] = $chron->status != CHRON_PLAY ? '' : '<a href="?action=pause&id='. $chron->id .'">Interromper</a>';
    $td[] = '<a href="?action=delete&id='. $chron->id .'">Destruir</a>';
    $td[] = '<a href="javascript:setChronometer(\''. $chron->id .'\')">Padrão</a>';
    $td[] = $chron->elapsed;
    echo '<tr id="cron-'. $chron->id .'"><td>'. implode('</td><td>', $td) .'</td></tr>';
}

?>
</table>
<a href="?action=new">Novo cronômetro</a>
</body>
</html>
  • The use will be per user. How would you differentiate one user from another? Beautiful code!

  • 1

    Thank you. I made this code only to meet your need, as I realized in the previous comments that there was a lot of confusion about your real need. As you can see in the code, you can obtain the information of a given stopwatch by requesting ?action=get&id={id}. But edit the answer to a more specific solution.

  • Actually, it’s not very secret, just link your user to the ID of a stopwatch created with the PHP Persistentchronometer::create() function. The class is very simple and very versatile, if you analyze the code of the small manager that accompanies, you will soon understand and can implement in your project.

2

I believe that if you can use the attributes of localStorage() to come up with a solution! localStorage is a function implemented in more up-to-date browsers (óbvil other than IE) where the browser saves information about that domain accessed within a variable, so you can pick up and save information in the variable when the page is closed and when you open it, fetch the initial value of the click and based on that continue to count the timer. Variable records are only deleted when rewritten or when browser cache is cleared.

  • 1

    I see a problem in this case if the user can access the system from another browser/ machine.

1

You could store in the cookie or via Web SQL the initial value, the timestamp where you started your chronometer. When leaving the page the value would be saved in the user’s browser, and if you already have any data saved in the browser it would continue where it left off. It would also be interesting to store the state of your chronometer (running, paused).

  • This would be safer in terms of data loss than saving via database on your own server?

  • 1

    I see a problem in this case if the user can access the system from another browser/ machine.

  • So.............

  • No, it wouldn’t be safer, but it could be more efficient at first. What you can do is work both ways, on the browser and on the server, and from time to time synchronize both.

  • @Lucaspolo, I don’t know if you saw but had already brought this solution by quoting the localStorage() in the answer above

1

If you don’t need to worry about the user changing their variables in js I advise you to follow the @Lucaspolo solution, otherwise do something like this:

When starting the timer, js executes a request to the server that saves the current timestamp in the database (obtained from php, not js).

js keeps running the timer and at each given time interval, or when the user returns to the browser (after closing it)0 js requests the server to get the time elapsed from the beginning.

  • That awe, man!!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.