Qualtrics has a JavaScript guide on their site for using their API. I think it’s terrible. This is my attempt at making a useful guide for JavaScript in Qualtrics.

Embedded Data

Currently (5/11/21) Qualtrics can only read and write to variables already declared in the embedded data.

Write

Qualtrics.SurveyEngine.setEmbeddedData("my_var", 7);    // set my_var to 7

Read

Qualtrics recommends reading via their standard jQuery method ${}. This useful for single variables, but does not scale too well with variables etc.

let my_var = "${e://Field/my_var}" // what Qualtrics recommend
let my_var = Qualtrics.SurveyEngine.getEmbeddedData("my_var") // what I recommend

For example I had a question where I wanted to had 20 questions and I randomly wanted to give hints for a certain amount of the Qualtrics. I was able to do this in a scalable way by reading in the correct answers, q\*_ans_letter, show them selectively based on another varaible in my embedded data q\*_show.

I was able to read

    // population the buttons
    var i;
    var pre_answer = "The correct answer is: ";
    var no_answer = "No help available";

for (i = 1; i <= 20; i++) {
    let ans_q = "q" + i + "_ans_letter" // make embedded variable name
    let show_q = "q" + i + "_show" // make embedded variable name
    var ans = Qualtrics.SurveyEngine.getEmbeddedData(ans_q) // read in answer
    var show = parseInt(Qualtrics.SurveyEngine.getEmbeddedData(show_q)); // show?

// write my answers to embedded variables accordingly
  if (show == 1){
    Qualtrics.SurveyEngine.setEmbeddedData("q" + i + "_text", pre_answer + " " + ans);
  } else if (show == 0){
      Qualtrics.SurveyEngine.setEmbeddedData("q" + i + "_text", no_answer);
  }
}

Recording Answers

I find it pretty useful to write answers to embedded data so that I can pipe them into a question on the next page.

Multiple Choice (recoded)

https://www.qualtrics.com/community/discussion/3883/set-embedded-data-from-this-questionid-selectedchoicesrecode

Qualtrics.SurveyEngine.addOnPageSubmit(function() {
    let selectedRecode = this.getChoiceRecodeValue(this.getSelectedChoices());
    Qualtrics.SurveyEngine.setEmbeddedData("ans", selectedRecode);
});

Slider Value

Qualtrics.SurveyEngine.addOnPageSubmit(function() {
    let slider_value =jQuery("#"+this.questionId+" .ResultsInput:eq(0)").val()
    Qualtrics.SurveyEngine.setEmbeddedData("ans", slider_value);
});

Text Entry

Qualtrics.SurveyEngine.addOnPageSubmit(function(type)
{
    let qid = this.questionId;
    let question = document.getElementById('QR~' + qid);
      let input = question.value;
        // Qualtrics.SurveyEngine.setEmbeddedData("my_ans", input);
});

Modifying Sliders

Qualtrics has a few

Hiding the slider button until moved

var q = jQuery("#" + currentQID);
q.find(".handle").css('visibility', 'hidden'); // hide slider value until moved
q.find(".track").on("click", function() {
    jQuery(this).find(".handle").css('visibility', 'visible');
    enableEl.defer('NextButton');
}); 

Showing the current value of the slider

jQuery("input[type = hidden]").change(function() {
    let answer = parseInt(jQuery("#" + currentQID + " input.ResultsInput").eq(0).val());
    console.log(answer)
    current.html("I am <b>" + answer + "%</b> certain that the best estimate, given what \
I’ve been told, is <b>" + bold_text + "</b>.");
    
    var answer_temp = answer
    var id ="${e://Field/question}";
    id = parseInt(id);
    Qualtrics.SurveyEngine.setEmbeddedData("answer_"+id, answer_temp);
});

HTML <div id="current" style="text-align: center;"><span style="color:#ffffff;">.</span></div>

Loop and Merge

Loop and Merge with Even Randomization

Qualtrics doesn’t have a built in way to evenly randomize loop and merge. Here’s a hack on how to do it.

In a question before on a block before the loop and merge

Qualtrics.SurveyEngine.addOnload(function()
{
  var questionDiv = this.getQuestionContainer(); // get question
  questionDiv.style.display = "none"; // hide question
  this.clickNextButton(); // automatically advance

});

Misc.

Referencing Other Questions

Most of the Qualtrics JS we see is within question, but often it is useful to have one main question on a page that allows me to set the JS. I especially use this pattern in Loop & Merges with a lot of conditional questions.

let currentQID = this.questionId // we usually see this 
let Q2QID = Object.keys(Qualtrics.SurveyEngine.QuestionInfo)[1] // use the second question

Counting Errors

https://www.qualtrics.com/community/discussion/7570/how-can-i-count-errors

var question = $(this.questionId)
$(question).select('.ValidationError').each(function(error) {
    if (error.innerHTML != '') {
        var errors = parseInt(Qualtrics.SurveyEngine.getEmbeddedData("errors_q_1"))
        errors++;
        Qualtrics.SurveyEngine.setEmbeddedData("errors_q_1", errors);
    }
})

Showing a specific statement missed in forced response

https://www.qualtrics.com/community/discussion/3669/show-a-specific-statement-missed-in-forced-response

Qualtrics.SurveyEngine.addOnReady(function(){
var n=jQuery("#"+this.questionId+" .ChoiceRow").length;
  if(jQuery("#"+this.questionId+" .ValidationError").text()!=""){
      for(var i=0;i<n;i++){
          if(jQuery("#"+this.questionId+" .ChoiceRow:eq("+i+") td input[type='radio']:checked").length==0){
              jQuery("#"+this.questionId+" .ChoiceRow:eq("+i+")").css("background","lightpink");
          }
      } 
  }
});  

Randomly select without replacement

// randomly assign list1:list4 w/o replacement
var bucket = ["cc", "cs", "sc", "ss"]; // set conditions

function getRandomFromBucket() { // define function
    var randomIndex = Math.floor(Math.random()*bucket.length);
    return bucket.splice(randomIndex, 1)[0];
 }

// use function four times to assign all
Qualtrics.SurveyEngine.setEmbeddedData("list1", getRandomFromBucket());
Qualtrics.SurveyEngine.setEmbeddedData("list2", getRandomFromBucket());
Qualtrics.SurveyEngine.setEmbeddedData("list3", getRandomFromBucket());
Qualtrics.SurveyEngine.setEmbeddedData("list4", getRandomFromBucket());

Many conditions (switch statements)

A switch statement can take an input and conditionally assign variables. Here’s a case where I loop inputs into a switch statement to assign conditional embedded variables

// set embedded data conditional on list
for (var i=1;i<=4;i++) {  
  current_list = Qualtrics.SurveyEngine.getEmbeddedData("list"+i);
     
  switch (current_list) {
     case "cc":
        left = "Donation for GiveDirectly"
        right ="Donation for GiveDirectly"
        prefix = "cc"  
        break;
     case "cs":
        left ="Donation for GiveDirectly"
        right ="Bonus for You"
        prefix = "cs"  
        break;
     case "sc":
        left ="Bonus for You"
        right = "Donation for GiveDirectly"
        prefix = "sc"  
        break;
     case "ss":
        left ="Bonus for You"
        right ="Bonus for You"
        prefix = "Bonus for You"  
        break;
     default:
        break;
    }

    Qualtrics.SurveyEngine.setEmbeddedData("prefix"+i, prefix);    
    Qualtrics.SurveyEngine.setEmbeddedData("left"+i, left);
    Qualtrics.SurveyEngine.setEmbeddedData("right"+i, right);
  }

“click here” button

Here we create a click here link that opens up a text box with additional information. Clicking on the box is recorded.

The following code will append a “click here” link to the end of the text. If the code in <div> tags there will be a linebreak. To stop the linebreak the HTML needs to be in <span> tags

Qualtrics.SurveyEngine.addOnload(function()
{
a = document.createElement('a');
a.id = "more_info"

a.innerHTML =  "click here.";
    
message  = "When asked to guess the percent chance of some outcome is true, \
let's call your guess G. To determine whether you earn $1 from that guess, a computer \
will randomly select a number N from 1 to 100. If that number is less than or equal \
to your guess, G, you will earn $1 if your guess is right.  If that number, N, is greater \
than your guess, you will instead earn $1 with a N% chance. This means that, \
to secure the largest chance of earning $1 from your guess, you should \
report your most-accurate guess."


a.setAttribute('href', "javascript:;");
a.setAttribute("onclick", "alert(message)")


x = jQuery("#"+this.questionId+" .QuestionText")
x.html(x.html() + " " + a.outerHTML)
    
    
jQuery("#more_info").click(function() {
   Qualtrics.SurveyEngine.setEmbeddedData("clicked_more_info", 1);
});
    
});

Reference Surveys

Suppose you ask the same demographic on every survey. You could imagine importing all of the questions at once. This is the correct thing to do in most cases. The questions will remain static, and for posterity you’ll be always be able to reference as it was. But suppose you were running multiple congruent surveys and wanted to make changes to them all globally? In this case, it could make sense to instead reference the Demographic survey.

Acknowledgements

Countless StackOverflow users, Qualtrics community members, but especially TomG.