为什么我的Qualtrics Javascript代码无法运行预期的迭代次数?

时间:2019-03-13 23:31:33

标签: javascript html qualtrics

我目前正在尝试进行定性的心理测试,称为IAT。我从名为IATgen的软件包中获取了一些代码,该软件包在R中构建了javascript IAT,然后可以在Qualtrics中使用。我对代码进行了一些修改,使其可以在手机上运行。但是,下面的代码无法正常工作。该代码应运行12次试验,该试验等于刺激数组的长度。但是,在运行代码时,在结束IAT之前仅进行1次试用。

我注意到的一件事是减少了image_srcs数组的长度,增加了试验次数,但我不明白为什么。但是,image_srcs数组的长度应独立于试验,因为它仅取决于刺激数组的长度。如果有人对下面的代码为何只运行1个试用版而不是12个试用版有任何想法,并且对如何调整使其能运行12个代码有任何想法,我将不胜感激。

如果我可以提供进一步的说明或回答任何问题,请随时询问/发送消息。提前谢谢大家!

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 = "<br><br><br>";

  // 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 < _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 " + '<span style="' + 'font-weight: bold; color: red"' + ">X</span>. 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 = "<br><br><br>";
      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 = "<br><br><br>";
      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 < end) {
      shuffle(array);
      for (var j = 0; j < 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 < 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 < 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 < 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: "<b style='color:green'>Luca</b>",
      correct: 73,
      index: 15
    },
    {
      stimulus: "<b style='color:green'>Andrea</b>",
      correct: 73,
      index: 16
    },
    {
      stimulus: "<b style='color:green'>Marco</b>",
      correct: 73,
      index: 17
    },
    {
      stimulus: "<b style='color:green'>Antonio</b>",
      correct: 73,
      index: 18
    },
    {
      stimulus: "<b style='color:green'>Paolo</b>",
      correct: 73,
      index: 19
    },
    {
      stimulus: "<b style='color:green'>Alberto</b>",
      correct: 73,
      index: 20
    }
  ];

  Bstim = [{
      stimulus: "<b style='color:green'>Giulia</b>",
      correct: 69,
      index: 21
    },
    {
      stimulus: "<b style='color:green'>Sofia</b>",
      correct: 69,
      index: 22
    },
    {
      stimulus: "<b style='color:green'>Chiara</b>",
      correct: 69,
      index: 23
    },
    {
      stimulus: "<b style='color:green'>Silvia</b>",
      correct: 69,
      index: 24
    },
    {
      stimulus: "<b style='color:green'>Lucia</b>",
      correct: 69,
      index: 25
    },
    {
      stimulus: "<b style='color:green'>Anna</b>",
      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);

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<style>
  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;
  }
</style>

<div class="border">


  <div class="lableft" id="left">
    <br><br>SCIENTIFICO<br><br>
  </div>


  <div class="labright" id="right">
    <br><br>UMANISTICO<br><br>
  </div>


  <h2 class="center" id="message"><br><br><br></h2>
  <h2 class="err" id="error"></h2>

  <div class="load">
    <p id="loading">Loading all the content...
      <br>
      <br> This should take less than 1 minute</p>
  </div>

  <div class="instruct" id="instructions">
    <p> Osserva che le parole in basso sono cambiate. Il compito da svolgere è lo stesso. Tocca "SCIENTIFICO" o "UMANISTICO" in base alla parola che apparirà.
      <br>
      <br> RISPONDI più velocemente possibile e cercando di fare pochi errori.
      <br>
      <br>
    </p>
  </div>

  <div class="avanti" id="avanti">
    <p>TOCCA QUI PER CONTINUARE
    </p>
  </div>


  <p class="note" id="note"></p>
</div>

<div style="opacity: 0; font-size:300px; color:#fff;">
  <p>x</p>
</div>

0 个答案:

没有答案