title: Why does my Javascript code for Qualtrics not run the intended number of iterations?
body: <p>I am currently trying to run a psychological test known as the IAT in qualtrics. I have taken some code from a packages called IATgen, which builds javascript IATs in R that can then be used in Qualtrics. I made some amendments to the code so that it will run on a phone. However, the code below does not function as intended. The code should run 12 trials, which is equal to the length of the stimuli array. However, when running the code, only 1 trial occurs before ending the IAT.</p>
  
  <p>One thing I noticed was reducing the length of the image_srcs array increases the number of trials but I cannot understand why. However, the length of the image_srcs array should be independent of the trials run as it should only depend on the length of the stimuli array. If anybody has any ideas why the code below only runs 1 trial instead of 12 and has any ideas on how to adjust it so that 12 runs, I would really appreciate knowing. </p>
  
  <p>If I can provide further clarification or answer any questions, please don't hesitate to ask/send a message. Thank you all so much in advance!</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>Qualtrics.SurveyEngine.addOnload(function() {
  
    //DECLARE INITIAL VARIABLES
    var currentStimulus;
    var end;
    var image_srcs;
    var images;
    var loadedImages;
    var input;
    var message;
    var stimuli;
    var note;
    var posstim;
    var negstim;
    var Astim;
    var Bstim;
  
    //USED FOR ALTERNATING TRIAL FORMAT ONLY
    var tgts;
    var cats;
    var tgtnum = [];
    var catnum = [];
  
    //DEFINE addlines. THIS WILL BE PUT IN FRONT OF WORD STIMULI TO DROP THEM DOWN TO BETTER ALIGN WITH IMAGE CENTERS.
    var addlines = "&lt;br&gt;&lt;br&gt;&lt;br&gt;";
  
    // THE FOLLOWING ARE ONLY USED IF FORCED ERROR CORRECTION IS ENABLED
    var fix = 0;
    var error;
  
    // CLEAN qID VARIABLE OF CHARACTERS AND SAVE AS NUMERIC FOR LATER USE
    var qID = this.questionId;
    qID = qID.replace("QID", '');
    qID = parseInt(qID);
  
    // GRAB INPUTID AS REFERENCE TO QUESTION AND HIDE TEXT BOX
    var InputId = document.getElementById("QR~" + this.questionId);
    InputId.style.display = "none";
  
    // HIDE NEXT BUTTON
    if (document.getElementById('NextButton')) document.getElementById('NextButton').style.display = "none"; //OLD API ...MAY NOT WORK NOW
    if (document.getElementById('PrevButton')) document.getElementById('PrevButton').style.display = "none"; //OLD API ...MAY NOT WORK NOW
    this.hideNextButton();
  
  
    //DECLARE FUNCTIONS
  
    //SHUFFLER - RANDOMIZES CONTENTS OF AN ARRAY USING A WELL VALIDATED METHOD
    function shuffle(array) {
      var currentIndex = array.length,
        temporaryValue, randomIndex;
  
      // While there remain elements to shuffle...
      while (0 !== currentIndex) {
  
        // Pick a remaining element...
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex -= 1;
  
        // And swap it with the current element.
        temporaryValue = array[currentIndex];
        array[currentIndex] = array[randomIndex];
        array[randomIndex] = temporaryValue;
      }
  
      return array;
    };
  
  
    // FUNCTION 1 - IMAGE LOADER
    /* This function is the first command in the IAT. It is invoked by code at the bottom of this script
    and puts all image stimuli in an array called images. If the image_srcs object has no URLs in it, this skips ahead
    to the next portion of the IAT without loading any images. If errors are encountered, the IAT block is skipped. */
    function loadImages(image_srcs) {
      var src, _i, _len;
  
      //If no images specified, skip this step
      if (image_srcs.length == 0) {
        return imagesLoaded();
      }
  
      images = [];
      loadedImages = 0;
  
      for (_i = 0, _len = image_srcs.length; _i &lt; _len; _i++) {
        src = image_srcs[_i];
        images.push(new Image);
  
        images[images.length - 1].src = src;
  
        images[images.length - 1].onerror = function() {
          alert("Your web browser encountered an issue running this portion of the study. You will be skipped ahead. You may have to click through this message several times.");
          if (document.getElementById('NextButton')) document.getElementById('NextButton').click();
        };
  
        images[images.length - 1].onload = function() {
          loadedImages++;
          if (loadedImages = images.length) return imagesLoaded();
        };
  
      }
      return images;
    };
  
  
    // FUNCTION 2 - IMAGES LOADED
    /* Runs when all image loader is finished starts the keypress listener.
    For a forced-error-correction IAT, the last line should return keyCheckForcedError instead of keyCheck*/
    function imagesLoaded() {
      document.getElementById('loading').style.display = 'none';
      document.getElementById('instructions').style.display = 'block';
  
      jQuery("#avanti").click(function() {
        return start();
      });
  
      jQuery("#left").click(function() {
        return keyCheckForcedErrorl();
      });
      jQuery("#right").click(function() {
        return keyCheckForcedErrorr();
      });
  
    };
  
  
    // FUNCTION 3 - START FUNCTION
    /* Runs when the spacebar is pressed after the keypress listener has begun. Does initial housekeeping (grabs HTML content such as message, error, etc. and makes it so we can write to them). It sets as 'input' the contents of the question text box,
    so we can write data to the question by editing that value. It also sets instructions to null. It then shuffles stimuli and
    starts the first trial. */
    function start() {
      message = document.getElementById("message");
      error = document.getElementById("error"); //USED ONLY WITH FORCED ERROR CORRECTION
      input = document.getElementById("QR~QID" + qID);
  
      instructions.innerHTML = "";
      avanti.innerHTML = "";
  
      note = document.getElementById("note");
      note.innerHTML = "Se commetti un errore, comparirà una " + '&lt;span style="' + 'font-weight: bold; color: red"' + "&gt;X&lt;/span&gt;. Correggi premendo l’altra categoria.";
  
  
  
      return nextQuestion();
    };
  
  
    // FUNCTION 4 - LAUNCHES QUESTION
    /* This function runs on start() and after a new trial begins. If we have not depleted the stimuli object, it grabs (and removes)
    the last trial from the stimuli object and calls it currentStimulus, then proceeds to use it for an IAT trial. Start time is identified and the stimulus is shown (different methods for images or words). If the stimuli object is depleted, appends END
    to data and advances to the next IAT block. */
    function nextQuestion() {
      if (stimuli.length !== 0) {
  
        currentStimulus = stimuli.pop();
  
        // SET MESSAGE TO EMPTY
        message.innerHTML = "";
  
        // DECLARE START OF CURRENT STIMULUS
        currentStimulus.start = new Date().getTime();
  
        // FOR IMAGES, USE APPEND CHILD TO DISPLAY. IF NOT, ADD STIMULUS VALUE TO MESSAGE.
        if (typeof currentStimulus.stimulus === 'object') {
          return message.appendChild(currentStimulus.stimulus);
        } else {
          return message.innerHTML += addlines + currentStimulus.stimulus;
        }
  
      } else {
  
        //WHEN STIMULI HAS NO TRIALS REMAINING, APPEND END, DISABLE KEY LISTENER, AND CLICK NEXT BUTTON
        /* IMPORTANT: In Qualtrics, the keypress listener stays active from one page to the next and
        must therefore be disabled before continuing. */
        input.value += "END";
        if (document.getElementById('NextButton')) document.getElementById('NextButton').click();
      }
    };
  
  
  
    // FUNCTION 5 - KEYPRESS LISTENER FOR FORCED ERROR CORRECTION MODE
    /* This is an alternate form of the same function, but it (1) only writes data when a correct response is entered (2)
    on errors, displays a red X that remains until the correct response is entered, and (3) scores as an error if the initial responses were incorrect (Greenwald et al., 2003). This should be scored without an error penalty as correcting the response naturally builds in an error.  */
  
    function keyCheckForcedErrorl(e) {
      var keyCode;
  
      keyCode = 69;
  
      // IF NO CURRENT STIMULUS (ONLY HAPPENS PRIOR TO START OF IAT), SPACEBAR CAN START IAT
  
      // NEXT, END FUNCTION IF NOT E OR I KEYS. IF CONTINUING AFTER THIS, MUST BE E OR I KEYPRESS.
      if (!(keyCode === 69 || keyCode === 73)) return;
  
      // note - do NOT grab timing here as it may be an error response
  
      //IF THE KEYCODE MATCHES THE CORRECT PART OF CURRENT STIMULUS, WRITE TO DATA AND DO OTHER STEPS
      //FORCED ERROR CORRECTION MODE.
      if (keyCode === currentStimulus.correct) {
  
        // Score the timing and save it
        currentStimulus.end = new Date().getTime();
        currentStimulus.reactionTime = currentStimulus.end - currentStimulus.start;
  
        if (fix == 0) {
          input.value += currentStimulus.index + "C" + currentStimulus.reactionTime + ",";
        }
  
        if (fix == 1) {
          input.value += currentStimulus.index + "X" + currentStimulus.reactionTime + ","; // score as error if we had to correct
        }
  
        message.innerHTML = "&lt;br&gt;&lt;br&gt;&lt;br&gt;";
        fix = 0;
        currentStimulus = null;
        error.innerHTML = "";
        return setTimeout(function() {
          return nextQuestion();
        }, 250);
  
      } else {
        error.innerHTML = "X";
        fix = 1;
        keyCode = 0;
        return;
      }
    };
  
    function keyCheckForcedErrorr(e) {
      var keyCode;
  
      keyCode = 73;
  
      // NEXT, END FUNCTION IF NOT E OR I KEYS. IF CONTINUING AFTER THIS, MUST BE E OR I KEYPRESS.
      if (!(keyCode === 69 || keyCode === 73)) return;
  
      // note - do NOT grab timing here as it may be an error response
  
      //IF THE KEYCODE MATCHES THE CORRECT PART OF CURRENT STIMULUS, WRITE TO DATA AND DO OTHER STEPS
      //FORCED ERROR CORRECTION MODE.
      if (keyCode === currentStimulus.correct) {
  
        // Score the timing and save it
        currentStimulus.end = new Date().getTime();
        currentStimulus.reactionTime = currentStimulus.end - currentStimulus.start;
  
        if (fix == 0) {
          input.value += currentStimulus.index + "C" + currentStimulus.reactionTime + ",";
        }
  
        if (fix == 1) {
          input.value += currentStimulus.index + "X" + currentStimulus.reactionTime + ","; // score as error if we had to correct
        }
  
        message.innerHTML = "&lt;br&gt;&lt;br&gt;&lt;br&gt;";
        fix = 0;
        currentStimulus = null;
        error.innerHTML = "";
        return setTimeout(function() {
          return nextQuestion();
        }, 250);
  
      } else {
        error.innerHTML = "X";
        fix = 1;
        keyCode = 0;
        return;
      }
    };
  
  
  
    //FUNCTION 6 - TAKES CONTENTS FROM A STIMULI POOL AND PLACES INTO PORTION OF AN OBJECT
    /* This function takes items from a given stimuli pool and places it randomly into portions of a destination object (positions between start and end). This is used, for example, to take portions of position and put it into a portion of the final stimuli object, or to move contents into intermediate objects that can then be placed (in alternating order) into a
    final stimuli object. */
  
    function stimBuilder(array, destination, start, end) {
      var i = start;
      while (i &lt; end) {
        shuffle(array);
        for (var j = 0; j &lt; array.length; j++) {
          destination[i].stimulus = array[j].stimulus;
          destination[i].index = array[j].index;
          destination[i].correct = array[j].correct;
          i++;
          if (i === end) {
            return;
          }
        }
      }
    }
  
  
  
  
    //FUNCTION 7 - FOR COMBINED BLOCKS WITH ALTERNATING FORMAT ONLY
    /* For combined blocks with an alternating form, this function is used to transfer stimuli from
    intermediary pools (cats and tgts) into the final stimuli object in an alternating format */
    function altStimuil() {
  
      //CREATE INDICES OF ALTERNATING NUMBERS TO USE FOR TGT AND CAT TRIALS
      var j = 0;
      for (var i = 1; i &lt; stimuli.length; i += 2) {
        tgtnum[j] = i; // starts at 1
        catnum[j] = (i - 1); // starts at 0
        j++; // represents position in tgt/cat number array
      }
  
      // FOR ALL TARGETS, MOVE CONTENTS FROM TARGET INTO STIMULI USING TGTNUM TO INDEX TRIALS
      for (var i = 0; i &lt; tgts.length; i++) {
        var alternating = tgtnum[i];
        stimuli[alternating].stimulus = tgts[i].stimulus;
        stimuli[alternating].correct = tgts[i].correct;
        stimuli[alternating].index = tgts[i].index;
      }
  
      // FOR ALL CATEGORIES, MOVE CONTENTS FROM TARGET INTO STIMULI USING CATNUM TO INDEX TRIALS
      for (var i = 0; i &lt; cats.length; i++) {
        var alternating = catnum[i];
        stimuli[alternating].stimulus = cats[i].stimulus;
        stimuli[alternating].correct = cats[i].correct;
        stimuli[alternating].index = cats[i].index;
      }
    }
  
  
  
    //  IAT CONTENTS
  
    //IMAGE URLS
    /* Consists of all pos, neg, A, and B images (in that order). */
    image_srcs = ["https://i.imgur.com/mPcVyLJ.png?2",
      "https://i.imgur.com/EGl6SXb.jpg?2",
      "https://i.imgur.com/NTqPRz2.jpg?1",
      "https://i.imgur.com/6Afxi4m.jpg?1",
      "https://i.imgur.com/BJvl9e1.png?2",
      "https://i.imgur.com/JRHYPaj.jpg?1",
      "https://i.imgur.com/gYays9U.jpg?1",
      "https://i.imgur.com/shEAiNT.jpg?1",
      "https://i.imgur.com/atATaXs.jpg?1",
      "https://i.imgur.com/GfIKphE.jpg?1",
      "https://i.imgur.com/kIZUBLw.jpg",
      "https://i.imgur.com/ciHxTt6.jpg?1"
    ];
  
  
    // THIS IS WHAT STARTS THE IAT
    /* This line of code triggers the IAT by running the first function. Note that the code skips image loading if image_srcs
    is empty. Note that this must come before stimuli in order that images[] references are defined.  */
    images = loadImages(image_srcs);
  
    // STIMULI POOLS
    posstim = [{
        stimulus: images[0],
        correct: 69,
        index: 1
      },
      {
        stimulus: images[1],
        correct: 69,
        index: 2
      },
      {
        stimulus: images[2],
        correct: 69,
        index: 3
      },
      {
        stimulus: images[3],
        correct: 69,
        index: 4
      },
      {
        stimulus: images[4],
        correct: 69,
        index: 5
      },
      {
        stimulus: images[5],
        correct: 69,
        index: 6
      }
    ];
  
    negstim = [{
        stimulus: images[6],
        correct: 73,
        index: 8
      },
      {
        stimulus: images[7],
        correct: 73,
        index: 9
      },
      {
        stimulus: images[8],
        correct: 73,
        index: 10
      },
      {
        stimulus: images[9],
        correct: 73,
        index: 11
      },
      {
        stimulus: images[10],
        correct: 73,
        index: 12
      },
      {
        stimulus: images[11],
        correct: 73,
        index: 13
      },
    ];
  
    Astim = [{
        stimulus: "&lt;b style='color:green'&gt;Luca&lt;/b&gt;",
        correct: 73,
        index: 15
      },
      {
        stimulus: "&lt;b style='color:green'&gt;Andrea&lt;/b&gt;",
        correct: 73,
        index: 16
      },
      {
        stimulus: "&lt;b style='color:green'&gt;Marco&lt;/b&gt;",
        correct: 73,
        index: 17
      },
      {
        stimulus: "&lt;b style='color:green'&gt;Antonio&lt;/b&gt;",
        correct: 73,
        index: 18
      },
      {
        stimulus: "&lt;b style='color:green'&gt;Paolo&lt;/b&gt;",
        correct: 73,
        index: 19
      },
      {
        stimulus: "&lt;b style='color:green'&gt;Alberto&lt;/b&gt;",
        correct: 73,
        index: 20
      }
    ];
  
    Bstim = [{
        stimulus: "&lt;b style='color:green'&gt;Giulia&lt;/b&gt;",
        correct: 69,
        index: 21
      },
      {
        stimulus: "&lt;b style='color:green'&gt;Sofia&lt;/b&gt;",
        correct: 69,
        index: 22
      },
      {
        stimulus: "&lt;b style='color:green'&gt;Chiara&lt;/b&gt;",
        correct: 69,
        index: 23
      },
      {
        stimulus: "&lt;b style='color:green'&gt;Silvia&lt;/b&gt;",
        correct: 69,
        index: 24
      },
      {
        stimulus: "&lt;b style='color:green'&gt;Lucia&lt;/b&gt;",
        correct: 69,
        index: 25
      },
      {
        stimulus: "&lt;b style='color:green'&gt;Anna&lt;/b&gt;",
        correct: 69,
        index: 26
      }
    ];
  
  
  
    //EMPTY SET OF TRIALS - LOADS FROM POOLS ABOVE
    stimuli = [{
        stimulus: "",
        correct: "",
        index: ""
      },
      {
        stimulus: "",
        correct: "",
        index: ""
      },
      {
        stimulus: "",
        correct: "",
        index: ""
      },
      {
        stimulus: "",
        correct: "",
        index: ""
      },
      {
        stimulus: "",
        correct: "",
        index: ""
      },
      {
        stimulus: "",
        correct: "",
        index: ""
      },
      {
        stimulus: "",
        correct: "",
        index: ""
      },
      {
        stimulus: "",
        correct: "",
        index: ""
      },
      {
        stimulus: "",
        correct: "",
        index: ""
      },
      {
        stimulus: "",
        correct: "",
        index: ""
      },
      {
        stimulus: "",
        correct: "",
        index: ""
      },
      {
        stimulus: "",
        correct: "",
        index: ""
      }
    ];
  
  
    //BUILD TRIALS
  
    var half = stimuli.length / 2;
    var cutoffs = [0, half, stimuli.length];
  
    stimBuilder(posstim, stimuli, cutoffs[0], cutoffs[1]);
    stimBuilder(negstim, stimuli, cutoffs[1], cutoffs[2]);
  
    shuffle(stimuli);
    shuffle(stimuli);
  
  });</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"&gt;&lt;/script&gt;
  &lt;style&gt;
    div.border {
      position: relative;
      width: 575px;
      height: 775px;
      border: 0px solid black;
    }
    
    div.labright {
      position: absolute;
      bottom: 5%;
      right: 1%;
      line-height: 110%;
      font-size: 20px;
      font-weight: bold;
      text-align: center;
      font-family: arial;
      color: blue;
    }
    
    div.lableft {
      position: absolute;
      bottom: 5%;
      left: 1%;
      line-height: 110%;
      text-align: center;
      font-size: 20px;
      font-weight: bold;
      font-family: arial;
      z-index: 1;
      color: blue;
    }
    
    div.avanti {
      position: absolute;
      bottom: 35%;
      width: 100%;
      text-align: center;
      font-size: 30px;
      font-weight: bold;
      font-family: arial;
      z-index: 1;
    }
    
    div.load {
      position: absolute;
      top: 55%;
      width: 100%;
      display: block;
      text-align: center;
      font-size: 16px;
      font-weight: normal;
      font-family: "Times New Roman";
    }
    
    div.instruct {
      position: absolute;
      text-align: center;
      top: 5%;
      font-size: 16px;
      font-family: "Times New Roman";
      display: none;
      line-height: 110%;
      padding-top: 10px;
      padding-right: 10px;
      padding-bottom: 10px;
      padding-left: 10px;
    }
    
    h2.center {
      position: absolute;
      text-align: center;
      width: 100%;
      line-height: 100%;
      top: 29%;
      font-size: 30px;
      font-weight: bold;
      font-family: arial;
    }
    
    p.note {
      position: absolute;
      text-align: center;
      top: 1%;
      font-size: 16px;
      width: 100%;
      line-height: 100%;
      font-family: "Times New Roman";
    }
    
    h2.err {
      position: absolute;
      display: block;
      text-align: center;
      width: 100%;
      line-height: 100%;
      top: 10%;
      font-size: 80px;
      font-weight: bold;
      font-family: arial;
      color: red;
    }
  &lt;/style&gt;
  
  &lt;div class="border"&gt;
  
  
    &lt;div class="lableft" id="left"&gt;
      &lt;br&gt;&lt;br&gt;SCIENTIFICO&lt;br&gt;&lt;br&gt;
    &lt;/div&gt;
  
  
    &lt;div class="labright" id="right"&gt;
      &lt;br&gt;&lt;br&gt;UMANISTICO&lt;br&gt;&lt;br&gt;
    &lt;/div&gt;
  
  
    &lt;h2 class="center" id="message"&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/h2&gt;
    &lt;h2 class="err" id="error"&gt;&lt;/h2&gt;
  
    &lt;div class="load"&gt;
      &lt;p id="loading"&gt;Loading all the content...
        &lt;br&gt;
        &lt;br&gt; This should take less than 1 minute&lt;/p&gt;
    &lt;/div&gt;
  
    &lt;div class="instruct" id="instructions"&gt;
      &lt;p&gt; Osserva che le parole in basso sono cambiate. Il compito da svolgere è lo stesso. Tocca "SCIENTIFICO" o "UMANISTICO" in base alla parola che apparirà.
        &lt;br&gt;
        &lt;br&gt; RISPONDI più velocemente possibile e cercando di fare pochi errori.
        &lt;br&gt;
        &lt;br&gt;
      &lt;/p&gt;
    &lt;/div&gt;
  
    &lt;div class="avanti" id="avanti"&gt;
      &lt;p&gt;TOCCA QUI PER CONTINUARE
      &lt;/p&gt;
    &lt;/div&gt;
  
  
    &lt;p class="note" id="note"&gt;&lt;/p&gt;
  &lt;/div&gt;
  
  &lt;div style="opacity: 0; font-size:300px; color:#fff;"&gt;
    &lt;p&gt;x&lt;/p&gt;
  &lt;/div&gt;</code></pre>
  </div>
  </div>
  </p>