#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