oTree Forum >

Click back in browser resets the timer

#1 by louaig

Hi all,

We've noticed that on clicking the back button on the browser it resets the clock. It seems like a core bug.
Is there a way of preventing it? 

Maybe I can save the timer on page onload in sessionStorage based on a certain page ID (if such thing exists), and reinitialize it on page load, but will need help in knowing the API of the timer. 

Any help is much appreciated

#2 by Daniel_Frey

Hi all,

I discovered this problem still exists (I have oTree 5.10.4 installed).

Clicking the 'back' button on the browser resets the timer clock (I believe to the stored offset or something). Only when reloading the page is the correct timeout (as if you never pressed the back button) shown again. 
The problem is, when you don't want to reload the page, you can technically stay forever on a timed page.

@Chris_oTree, is this something you can fix, or is the problem with jQuery Countdown?

#3 by marissa_lepper (edited )

Hi everyone,

The timer issue is still happening, which can cause some big issues. Notably, participants are moved forward server-side after the original timer runs out, meaning that any interaction between the original timer and the refreshed one is not recorded. Outside of the data loss, this can potentially erode trust within the experiment. 

I wanted to share a quick fix that uses live pages to automatically refresh the page anytime a user reloads it. This isn't a perfect solution, notably:
-- All JavaScript information will be lost, including any participant interactions not already stored on the server. It will also restart any JS timing information 
-- Group interactions may not go through or be delayed if they happen while the page is being refreshed
-- It does not different between types of refreshes (so hitting refresh, which doesn't cause the timer issue, and hitting the back button will both trigger a forced reload).

But even so, I hope it's helpful for others in this situation!

--------------
Simple version:

Add the following JavaScript to TimedPage.html:

---
window.addEventListener('DOMContentLoaded', (event) => {
    liveSend(999);
});

function liveRecv(data) {
    if (data.type === 'refresh'){
        location.reload()
    }}
    
---

Add the following to __init__.py: 

---
class Player(BasePlayer):
    loads = models.IntegerField(doc="number of page loads", initial=0)
    force = models.IntegerField(doc="variable to ensure infinite refresh doesn't happen", initial=0)
    
# PAGES

class TimedPage(Page):
    timeout_seconds = 60 
    
    def live_method(player: Player, data):
        if data == 999:
            player.loads += 1

            if player.loads > 1:
                if player.loads > player.force:
                    player.force = player.loads + 1
                    response = dict(type='refresh')
                    return {player.id_in_group: response}
    
---

A slightly more complicated version (good for if you participants are completing real effort tasks on a timed live page):

------------
JavaScript for TimedPage.html:

---
window.addEventListener('DOMContentLoaded', (event) => {
    liveSend({'pageLoad': true});
});

sendbutton.onclick = function () {
    liveSend({'pageLoad': false, // any other information you want to send over when the button is hit
    });
};

function liveRecv(payload) {
    // add additional JS for the live send function here
    
    if (payload.refresh) {
        location.reload()
    }
    else if (payload.show) {
        // add any additional JS you want to be triggered if and only if it's a forced reload here
    }
}

---

On __init__.py:

---
class Player(BasePlayer):
    var1 = models.IntegerField(doc="variable from participant interaction with live page", initial=0)
    
    loads = models.IntegerField(doc="number of page loads", initial=0)
    force = models.IntegerField(doc="variable to ensure infinite refresh doesn't happen", initial=0)
   
# FUNCTIONS

def live_meth(player:Player, answer):
    refresh = False
    
    if answer.get('pageLoad'):
        player.loads += 1
        show = False

        if player.loads > 1:
            if player.loads > player.force:
                player.force = player.loads + 1
                refresh = True
            else:
                show = True
                
        # Any other code that you want run whenever the page loads (including the initial one) goes here 
        
        broadcast = {'refresh': refresh,
                     'show': show,
                     'var1': player.var1,
                     # Anything else you need to send over when the page is loaded
                     }
        return {player.id_in_group: broadcast}
        
    elif not answer.get('pageLoad'):
        # Any code that will be triggered by the live page itself
        
        broadcast = {'var1': player.var1,
                     # Anything else you need to send over -- don't include refresh variable here
                     }
        return {player.id_in_group: broadcast}

# PAGES

class TimedPage(Page):
    live_method = live_meth 
    timeout_second = 60

---------------------------------------------

#4 by Daniel_Frey

Hi Marissa

Thank you very much; your fix solves my problem!

I tried something similar but couldn't get around the infinite refresh loop.

Best,
Daniel

Write a reply

Set forum username