How to clear keydown function/functionality of a keypress? | XM Community
Skip to main content

I programmed my survey so that the enter key advances survey-takers through the questions.
I only had to use this JavaScript on one question, and it works for the whole survey.
However, I need to remove the functionality of this code for one of the questions. Any idea on how to clear the Event.observe() function? Note I will turn this functionality back on immediately after this question.
Survey Structure:
Page 1 (use enter key to advance)
Page 2 (use enter key to advance)
Page 3 (use enter key to advance)
Page 4 (other functionality specified to advance)
Page 5 (use enter key to advance)
Event.observe(document, 'keydown', function keydownCallback(e){

if (e.keyCode == 13)
{
that.clickNextButton();
}
})

Adding this to the unload function of Page 3 did not fix the problem:
document.onkeydown = function(event) {}

Neither did this:
Event.observe(document, 'keydown', function keydownCallback(e){

if (e.keyCode == 13)
{
}
})

qualtricsnewb
I believe if you are adding on all pages you're including JS in header. You can use the below codes in the places mentioned for achieving the desired flow.
Header -> Source for all pages:

Question where not needed include in JS:
Qualtrics.SurveyEngine.addOnReady(function() {
document.onkeydown = function(event) {
console.log('keydown',event);
if (event.which == 13) {
event.preventDefault();
}
}
});
Hope it helps!


qualtricsnewb ,
You should cancel the event in the addOnUnload() function. For example, using jQuery:
Qualtrics.SurveyEngine.addOnReady(function() {
  var qobj = this;
  jQuery(document).keydown(function(e) { if(e.key == "Enter") qobj.clickNextButton(); });
});
Qualtrics.SurveyEngine.addOnUnload(function() { jQuery(document).off("keydown"); });


https://community.qualtrics.com/XMcommunity/discussion/comment/51910#Comment_51910Thanks! But I need to keep
Event.observe(document, 'keydown', function keydownCallback(e){}
The survey structure is more complicated than the schematic I posted.

The same key press means different things for different pages (I didn't just program the enter key, I'm using it as the simplest example here to test).

I uploaded some survey blocks from IATgen and it uses the same keys that I use earlier in the survey, but calls them differently. You can see my comment below if you want to see the code.


Does anyone know how to clear the function I used from carrying over to subsequent questions?


https://community.qualtrics.com/XMcommunity/discussion/comment/51913#Comment_51913Thanks for responding!

I specifically need to clear out the functionality I created before. The enter key will work on Page 4 (so will keys E and I to make choices, they were also used for this on the other pages) , but the way it's used to advance is a little different.

This is why I need to specifically disable
Event.observe(document, 'keydown', function keydownCallback(e){

if (e.keyCode == 13)
{
that.clickNextButton();
}
})


Here is why I need to disable all keypress functions. I need to use this code for 7 blocks of IAT. However, the questions before the IAT use the same keys (my boss insists..).
I need to clear the functionalities of the keypress before the IAT. So the question is: How do I clear this function for subsequent questions?
Event.observe(document, 'keydown', function keydownCallback(e){})

IAT:

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


";

// 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';
return document.addEventListener('keyup', keyCheckForcedError, false);
};


// FUNCTION 3 - START FUNCTION
/* Runs when the spacebar is pressed after the keypress listener has begun. Does initial houskeeping (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);

//MAKE INSTRUCTIONS EMPTY
instructions.innerHTML = "";

//ADD NOTE BELOW WINDOW
note = document.getElementById("note");
note.innerHTML = "";

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. A 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";
document.removeEventListener('keyup', keyCheckForcedError, false);
if (document.getElementById('NextButton')) document.getElementById('NextButton').click();
}
};


// FUNCTION 5 - KEYPRESS LISTENER FOR STANDARD ERROR MODE
/* This function grabs whatever key was pressed and saves it as keyCode. Depending on what was pressed, it will
start the IAT, write data, display next trials, etc. Note that errors are handled by swapping the message out
for a red X (the error message below the stimulus is NOT used). Note also that an alternative version of this
function is defined below but only ONE will be called in a given IAT.*/
function keyCheck(e) {
var keyCode;

/* depending on how this was triggered, grab keycode and save as keyCode. May need upgrading as web
standards improve for keypress handling. */
if (window.event) {
keyCode = event.keyCode;
} else {
keyCode = e.keyCode;
}

// IF NO CURRENT STIMULUS (ONLY HAPPENS PRIOR TO START OF IAT), SPACEBAR CAN START IAT
if (!currentStimulus) {
if (keyCode === 32) {
start();
}
return;
}

// 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;

// ADD END PROPRETY TO CURRENTSTIM
currentStimulus.end = new Date().getTime();

// CALCULATE DIFFERENCE AND SAVE
currentStimulus.reactionTime = currentStimulus.end - currentStimulus.start;

//IF THE KEYCODE MATCHES THE CORRECT PART OF CURRENT STIMULUS, WRITE TO DATA AND DO OTHER STEPS
//TRADITIONAL ERROR MODE
if (keyCode === currentStimulus.correct) {
input.value += currentStimulus.index + "C" + currentStimulus.reactionTime + ",";
message.innerHTML = "


+";
currentStimulus = null;
return setTimeout(function() {return nextQuestion(); }, 250);

} else {
input.value += currentStimulus.index + "X" + currentStimulus.reactionTime + ",";
message.innerHTML = "

X
";
currentStimulus = null;
setTimeout(function() {return message.innerHTML = "


+";}, 250);
return setTimeout(function() {return nextQuestion();}, 250);
}
};


// FUNCTION 5 - KEYPRESS LISTENER FOR FORCED ERROR CORRECTION MODE
/* This is an alternate form of 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 was incorrect (Greenwald et al., 2003). This should be scored without an error penalty as
correcting the response naturally builds in an error. */

function keyCheckForcedError(e) {
var keyCode;

/* depending on how this was triggered, grab keycode and save as keyCode. May need upgrading as web
standards improve for keypress handling. */
if (window.event) {
keyCode = event.keyCode;
} else {
keyCode = e.keyCode;
}

// IF NO CURRENT STIMULUS (ONLY HAPPENS PRIOR TO START OF IAT), SPACEBAR CAN START IAT
if (!currentStimulus) {
if (keyCode === 32) {
start();
}
return;
}

// 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 = "


+";
fix=0;
currentStimulus = null;
error.innerHTML = "";
return setTimeout(function() {return nextQuestion(); }, 250);

} else {
error.innerHTML = "X";
fix=1;
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 posstim 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 shuffle(array);
shuffle(array);
shuffle(array);
shuffle(array);
shuffle(array);
for(var j=0; 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://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_0vwyIGpMlaif8mq",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_1HddKm3aB9wSEK2",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_3q0KH24IH2GDSHc",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_3U7Yo9xps2jrn14",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_5oneGd2AAJpnEyy",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_6riOEN4fOIbAVtY",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_6Vwnz6EEBsGFOui",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_7Vr5ew23fjoDibs",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_b42z5M3xDxogAXY",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_dnHmmJkLHOMLLRs",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_0IHHrJDsreeYOa2",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_2hpPC6izas0hFrg",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_2raVat9rRKTwG8K",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_3fvFDTYxb1JpAHk",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_3JGmIt6RPmBMlPU",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_3lcfbdGi4AMgLz0",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_72j8an2Qp2miB7w",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_82L9PusCRSNzqKO",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_9QSbmS3STFOPsQ6",
"https://cornell.yul1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_ebOUYTbuphOd0i2"
];


// 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: "Fabulous", correct:"NA", index: 1},
{stimulus: "Fantastic", correct:"NA", index: 2},
{stimulus: "Great", correct:"NA", index: 3},
{stimulus: "Lovely", correct:"NA", index: 4},
{stimulus: "Nice", correct:"NA", index: 5},
{stimulus: "Superb", correct:"NA", index: 6},
{stimulus: "Terrific", correct:"NA", index: 7},
{stimulus: "Wonderful", correct:"NA", index: 8}
];

negstim = [
{stimulus: "Awful", correct:"NA", index: 9},
{stimulus: "Disgusting", correct:"NA", index: 10},
{stimulus: "Foul", correct:"NA", index: 11},
{stimulus: "Horrible", correct:"NA", index: 12},
{stimulus: "Horrid", correct:"NA", index: 13},
{stimulus: "Nasty", correct:"NA", index: 14},
{stimulus: "Revolting", correct:"NA", index: 15},
{stimulus: "Terrible", correct:"NA", index: 16}
];

Astim = [
{stimulus: images[0], correct:73, index: 17},
{stimulus: images[1], correct:73, index: 18},
{stimulus: images[2], correct:73, index: 19},
{stimulus: images[3], correct:73, index: 20},
{stimulus: images[4], correct:73, index: 21},
{stimulus: images[5], correct:73, index: 22},
{stimulus: images[6], correct:73, index: 23},
{stimulus: images[7], correct:73, index: 24},
{stimulus: images[8], correct:73, index: 25},
{stimulus: images[9], correct:73, index: 26}
];

Bstim = [
{stimulus: images[10], correct:69, index: 27},
{stimulus: images[11], correct:69, index: 28},
{stimulus: images[12], correct:69, index: 29},
{stimulus: images[13], correct:69, index: 30},
{stimulus: images[14], correct:69, index: 31},
{stimulus: images[15], correct:69, index: 32},
{stimulus: images[16], correct:69, index: 33},
{stimulus: images[17], correct:69, index: 34},
{stimulus: images[18], correct:69, index: 35},
{stimulus: images[19], correct:69, index: 36}
];



//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: ""},
{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(Astim, stimuli, cutoffs[0], cutoffs[1]);
stimBuilder(Bstim, stimuli, cutoffs[1], cutoffs[2]);

shuffle(stimuli);
shuffle(stimuli);




});


Not sure if you are using addOnload or addOnReady, but put addOnUnload inside whichever you are using then:
document.removeEventListener("keydown", keydownCallback);


https://community.qualtrics.com/XMcommunity/discussion/comment/51923#Comment_51923Unfortunately enter still advances the survey even after the question in which this code is put in
Qualtrics.SurveyEngine.addOnUnload(function()
{
document.removeEventListener("keydown", keydownCallback);

});
Even added it to Onload of the question for which I don't want "enter" to advance.


Adding this to the unload{function()} of the last keypress question did the trick!

Event.stopObserving(document, 'keydown');