Problems with Javascript timer | XM Community
Question

Problems with Javascript timer

  • 18 June 2020
  • 9 replies
  • 266 views

Hello, I am trying to set-up a timer that will be initiated upon starting a certain block within a survey. The timer should appear on the top of the screen regardless of scrolling and continue to count down as the participant clicks through different pages within the block. Upon the timer reaching 0, I want the survey to progress to a final question (a pseudo-end screen, it can be in the same block or in its own block if that helps) and upon this question loading the timer would no longer be visible.
I have very little programming experience so I am mostly just grabbing code I can find online and trying to adapt it. The code I have found is able to make a timer appear when the block starts and remain on screen despite scrolling but upon reaching 0 it just displays text and doesn't advance anything. It also persists at the top of the screen when at my "pseudo-end screen" as well as when the actual survey termination end screen appears.
The following code is added onto the first question of the block where I want it to start:
Qualtrics.SurveyEngine.addOnload(function()
{
var headerCont = document.createElement("div");  
 headerCont.className = "header-cont";  
 headerCont.id = "header_container";  
 var header = document.createElement("div");  
 header.className = "header"  
 header.id = "header_1";  
 var timer = document.createElement("div");  
 timer.className = "timer";  
 timer.id = "timer_1";  
 timer.innerHTML = "Time Remaining: 07:30";  
 headerCont.appendChild(header);  
 header.appendChild(timer);  
 document.body.insertBefore(headerCont, document.body.firstChild); 

function startTimer(duration, display) {  
 var timer = duration, minutes, seconds;  
 var myTimer = setInterval(function() {  
  minutes = parseInt(timer / 60, 10)  
  seconds = parseInt(timer % 60, 10);  
  minutes = minutes < 10 ? "0" + minutes : minutes;  
  seconds = seconds < 10 ? "0" + seconds : seconds;  
  var text = ('innerText' in display)? 'innerText' : 'textContent';
  display[text] = minutes + ":" + seconds;  
  if (--timer < 0) {  
  clearInterval(myTimer);  
  timeOver();  
  }  
 }, 1000);  
 }  
 var timerSeconds = 450,  
 display = document.querySelector('#time');  
 startTimer(timerSeconds, display);  
 var timeOver = function() {  
 document.getElementById("timer_1").innerHTML = "Time is up.";  
 x = 1;  
 var bgColor = setInterval(change, 1000);  
 }  
 
});
I also have the following custom CSS code under Style in "Look and Feel"
.header-cont {  
  width:100%;  
  position:fixed;  
  top:0px;  
  z-index:1000;
 }  
 .header {  
  height:auto;  
  background:#FFFFFF;  
  width:100%;  
  top: 0px;
margin:0px auto;
  position:fixed;
z-index:1000;
 }  
 .timer{  
  width: 500px;  
  margin: auto;  
  text-align: center;  
  vertical-align: middle;  
  line-height: 50px;    
  font-size: 28px;  
 }
Thank you for any help you can provide.


9 replies

Userlevel 7
Badge +27

This same faulty cut and paste timer code shows up regularly on the Community. It was referred to just yesterday. A number of people who don't know much JS have tried to use it and run into problems.
See: https://www.qualtrics.com/community/discussion/comment/26846#Comment_26846

I appreciate the response, I had seen that post prior to posting but being new to this whole system means I don’t fully understand it. I have managed to find out how to get the survey to auto advance to the place I want upon the timer reaching 0 by looking through these forums more thoroughly, although I am sure my solution isn’t very efficient or elegant (discussed below in case it could help anyone else.) 
However, I am still unclear on clearing the timer from the top of the screen.  The blog post I initially took this code from mentioned the distinction between static and dynamic themes possibly causing problems but it is outdated and I’m not getting the problem it describes. Currently, all of my Javascript is loaded on the first question of the block where I want the timer to appear, and that is it. Using dynamic or static themes doesn’t cause any problem but the timer persists after the block ends. The post you linked to mentions clearing the interval upon leaving the page and then executing the code on each page of the block. My code is slightly different from the code used in that post so I’m not sure if that will impact things and also I don’t know how to clear the interval. 
My assumption on what I might need to do is put the Javascript code onto the first (and/or maybe last) question of each page in the block where I want the timer to be active and also change the code (or add code somewhere, possibly under “Qualtrics.SurveyEngine.addOnUnload(function()” that when the timer is less than 0, the timer interval gets cleared. The code has a clearInterval line in it already:
function startTimer(duration, display) {  
 var timer = duration, minutes, seconds;  
 var myTimer = setInterval(function() {  
  minutes = parseInt(timer / 60, 10)  
  seconds = parseInt(timer % 60, 10);  
  minutes = minutes < 10 ? "0" + minutes : minutes;  
  seconds = seconds < 10 ? "0" + seconds : seconds;  
  var text = ('innerText' in display)? 'innerText' : 'textContent';
  display[text] = minutes + ":" + seconds;  
  if (--timer < 0) {  
  clearInterval(myTimer);  
  timeOver();  
  }  
 }, 1000);  
 }
but I don’t understand what it is doing and it might need to be changed as it comes from an outdated blog post. If someone could show me what code I need to insert (and where) to make the timer disappear from the top of the screen upon either of two conditions being met I think all of my issues would be resolved. The two conditions are:
(1) When the timer has reached 0 and the survey has auto advanced past the block where the timer matters (mentioned below) 
(2) when the participant manually advances the survey to the next block (basically completing the 100 questions before the timer expires).
Thanks again for the response and I hope someone can help me with my final issue.

How I got the auto advance to work
The important bit of the code (taken from my first post) is
var timeOver = function() {  
 document.getElementById("timer_1").innerHTML = "Time is up.";  
 x = 1;  
 var bgColor = setInterval(change, 1000);  
 }  
Also note I failed to copy the rest of the code from the blog post I took it from, but it doesn’t apply to my scenario as I don’t want the background color to flash red and white when the timer hits 0, I want the survey to advance to the end. In the above code the line starting with var bgColor… is all unneeded and can be removed.
I found another forum thread where auto advancing to a specific question was discussed (https://www.qualtrics.com/community/discussion/4508/javascript-instead-of-automatically-clicking-next-how-to-code-it-to-go-to-a-certain-question) For me, I have 100 questions total and upon the timer ending I want the survey to advance past all 100, so I set an embedded data value called “timeUp” with starting value of 0 into the survey flow before the block with the 100 questions. Then I manually set each of the 100 questions display logic to display if “timeUp” is Equal to 0 (this took a while and is why I mention it is probably inefficient). I then modified the important bit of code to this:
var timeOver = function() { 
 document.getElementById("timer_1").innerHTML = "Time is up."; 
 x = 1; 
Qualtrics.SurveyEngine.setEmbeddedData('timeUp', 1);
$('NextButton').click();
}
This makes the embedded value timeUp get set to 1 when the timer hits 0 and also automatically clicks the next button, which since none of the 100 items are able to be displayed zips the survey to the end where I want it.

Userlevel 7
Badge +27

The timer interval only gets cleared when the 'timer' is less than zero. When it never reaches zero it persists after the block ends...the Interval keeps going. Also, once it goes to a new page you longer have access to the 'timer' or 'myTimer' variables since those are local to the addOnload function on the original page.
That's why I said you have clear the interval on each page. Furthermore, the function that clears it must be within the addOnload function in order to have access to the 'myTimer' variable.

Thanks again for the continued help. As a complete newbie to JavaScript (and coding in general) I am not sure how to go about clearing the interval on each page as you say. I understand that currently the code
if (--timer < 0) {  
        clearInterval(myTimer);  
        timeOver();
}
is what you are referring to when you say it keeps going. My guess at a solution would be to add another if statement that executes when the participant clicks the next page button, and would write the current timer value into embedded data and then call clearInterval again on the timer. Then on each subsequent page, copy over the script that controls the timer and change it slightly so that instead of restarting the timer at whatever starting value it is set to, have it start at whatever value is stored in the embedded data. 
If these assumptions are correct, my problem is I don’t know what the syntax is to have code execute upon clicking the next page button. 
My guess at how to write the if statement currently is:
// (previous if statement up here)

if (--timer > 0 && $(‘NextButton’) {  // Missing some information
        Qualtrics.SurveyEngine.setEmbeddedData('timeRemaining', timer);
        clearInterval(myTimer);
}
This is missing something since I don’t know what the syntax is to make the code inside {} execute only when the NextButton is clicked.
Thanks again for all of the help!

Userlevel 7
Badge +27

Clear interval on next button click:
Qualtrics.SurveyEngine.addOnPageSubmit(function() {
Qualtrics.SurveyEngine.setEmbeddeData('timeRemaining',timer);
clearInterval(myTimer);
});
The addOnPageSubmit function must be inside addOnload function.

Thanks for the fast reply, I think my inexperience is really showing because the code you mention doesn’t work for me. I added it at the very end of the addOnload function and nothing happens to the timer when it loads the next page. For clarity, the entire JavaScript I have attached to the question on the first page is:
Qualtrics.SurveyEngine.addOnload(function()
{
var headerCont = document.createElement("div"); 
               headerCont.className = "header-cont"; 
               headerCont.id = "header_container"; 
var header = document.createElement("div"); 
               header.className = "header" 
               header.id = "header_1"; 
var timer = document.createElement("div"); 
               timer.className = "timer"; 
               timer.id = "timer_1"; 
               timer.innerHTML = "Time Remaining: 07:30"; // set actual time limit correctly here
headerCont.appendChild(header); 
header.appendChild(timer); 
document.body.insertBefore(headerCont, document.body.firstChild);

function startTimer(duration, display) { 
               var timer = duration, minutes, seconds; 
               var myTimer = setInterval(function() { 
                               minutes = parseInt(timer / 60, 10) 
                               seconds = parseInt(timer % 60, 10); 
                               minutes = minutes < 10 ? "0" + minutes : minutes; 
                              seconds = seconds < 10 ? "0" + seconds : seconds; 
                              var text = ('innerText' in display)? 'innerText' : 'textContent';
                              display[text] = minutes + ":" + seconds; 
                              if (--timer < 0) { 
                                               clearInterval(myTimer); 
                                               timeOver();
                               }
               }, 1000); 

var timerSeconds = 449,  // This needs to be 1 second shorter than the total time, as it seems to increment 1 late
display = document.querySelector('#time'); 
startTimer(timerSeconds, display); 
var timeOver = function() {
               document.getElementById("timer_1").innerHTML = "Time is up."; 
               x = 1; 
               Qualtrics.SurveyEngine.setEmbeddedData('timeUp', 1);
               $('NextButton').click();
 } 
Qualtrics.SurveyEngine.addOnPageSubmit(function() {
    Qualtrics.SurveyEngine.setEmbeddedData('timeRemaining',timer);
    clearInterval(myTimer);
});
});

Qualtrics.SurveyEngine.addOnReady(function()
{
//$('PreviousButton').hide();
$('PreviousButton').disable();
});

Qualtrics.SurveyEngine.addOnUnload(function()
{

               
});
I assume I have some other problem with the code somewhere that prevents what you just replied with from working. Currently running the above code without any JavaScript code on the next page’s set of questions leaves the timer running, which I assume is wrong since I believe the code you gave me should make the timer stop upon leaving the first page and require custom code to make it reappear on the next page. Also in your code above I think you missed a 'd' in EmbeddedData but if that is intentional it could also explain the problem.

Thanks again.

MRHelfrich Were you ever able to figure out how to clear the interval? I'm facing the same problem.

Userlevel 7
Badge +27

https://www.qualtrics.com/community/discussion/comment/29313#Comment_29313The problem with the code above is that myTimer is local to the startTimer function. It needs be to defined in the addOnload function.

https://www.qualtrics.com/community/discussion/comment/29313#Comment_29313I assume that my solution is overkill since I am sure there is a way to actually clear the interval like has been suggested, I just never could figure it out.
What I ended up doing was adding this code to the first question on each page:
Qualtrics.SurveyEngine.addOnPageSubmit(function() {
Qualtrics.SurveyEngine.setEmbeddedData('timeRemaining',timer);
clearInterval(myTimer); // fairly certain this isn't doing anything
});

Qualtrics.SurveyEngine.addOnUnload(function() {
document.getElementById("header_container").remove();
});
This takes the time left on the timer when the participant clicks the button to go to the next page and saves it as embedded data into Qualtrics. After that, my goal was to remove the entire timer object from the page, I’m not sure if that is what the code explicitly does but it worked for me. Then on the first question of the next page, the timer gets built but instead of reading in the initial value to start counting from, it reads in the value that was saved as embedded data.
The drawback is since it rebuilds each time, there is up to 1 second of extra time gained where the page is loaded but the timer isn’t. I think the value the timer saves is a whole second, so it can only restart at 1 whole second, so if the timer says 30 seconds left when the page changes, if there was 10ms until the timer would decrement to 29 seconds, it still saves it as 30, and then the page loads, then the timer, and then the timer starts counting down from 30 again. There are only a dozen or so page changes in my survey so it doesn’t amount to much overall but it is technically not precise.
I hope this helps.

Leave a Reply