How do I disable the next button until the last carousel question is answered? | XM Community
Solved

How do I disable the next button until the last carousel question is answered?

  • 13 September 2021
  • 11 replies
  • 785 views

Hello,
I am very new with Qualtrics and javascript.
Is there a way for my next button to hidden until/appear only after the last carousel question is answered?
Has anyone written a similar script and could help me with it?

icon

Best answer by JeremyK 14 September 2021, 16:10

View original

11 replies

Userlevel 5
Badge +7

You could do it with JS, yes. Attach a .click() eventListener to the carousel .QuestionBody with an index that starts at 0 and increments by 1 per click. Set your next button to be hidden by calling the Qualtrics question API hideNextButton(). Check the index each click and, once the number of carousel questions has been reached (e.g. index > 4), call showNextButton(). A few problems I foresee could crop up, but that's the short of it.
A much, much simpler solution would be to set this question to required and force a response, ensuring each question is answered before allowed respondents to continue to the next page (provided that partial answers is what you're trying to out of your data set).

Badge +1

For anyone else who comes looking for a solution to this. It counts the number of carousel questions, and then every time an answer button is clicked it simply checks whether the question being answered is the last question in the carousel.
Qualtrics.SurveyEngine.addOnReady(function()
{
jQuery("#NextButton").hide();

elAnswerButtons = document.querySelector('.CarouselAnswerButtonList');

var QuestionCount = document.querySelectorAll("div.CarouselCardFrame div[data-choiceid]").length;

elAnswerButtons.addEventListener("click", () => {
dispQ = document.querySelector('div.CarouselCard:not(.NoDisplay)').getAttribute('data-choiceid');
if(dispQ == QuestionCount)
{
jQuery("#NextButton").show();
}
});


});
This is more reliable that incrementing click counts or checking the property of the progress bar as I have seen suggested elsewhere and tried myself.
To break it down

  • The answer options are within the div with class CarouselAnswerButtonList

  • The questions are contained within the div with class CarouselCardFrame

  • the data-choiceid is the index of the questions. If there are 5 questions, the data-choiceid values are 1-5.

  • The question shown is a div with class CarouselCard, all of the questions that are hidden are divs with class "CarouselCard NoDisplay". So you get the data-choiceid attribute of the div with class "CarouselCard:not(.NoDisplay)" and you have the index of the displayed question.

  • Then you just test if the index of the displayed question is the same as the number of questions; if so then you have captured a click on an answer to the last question

Badge +4

I'm looking to do a similar thing. I want to auto-advance participants once they have reached the end of the carousel. A minor modification on the code above worked brilliantly (thank you!!). However, it all breaks down if I want to randomize the display of my questions. VaughanS - can you think of a work-around? I tried creating an index variable that counts the clicks and checks if the click count is equal to the question count, but it seems to be advancing way too early. Can you tell me what I'm doing wrong?
Here is my code right now:
Qualtrics.SurveyEngine.addOnReady(function()
{

elAnswerButtons = document.querySelector('.CarouselAnswerButtonList');

var QuestionCount = document.querySelectorAll("div.CarouselCardFrame div[data-choiceid]").length;
var index = 0;

elAnswerButtons.addEventListener("click", () => {

index = index + 1;

dispQ = document.querySelector('div.CarouselCard:not(.NoDisplay)').getAttribute('data-choiceid');
if(index == QuestionCount)
{
this.clickNextButton();
}
});


});

Userlevel 7
Badge +27

Avital It is working on my end by adding the click counter outside of the event listener that clicks the next button.
Qualtrics.SurveyEngine.addOnReady(function()
{
/*Place your JavaScript here to run when the page is fully displayed*/
jQuery("#NextButton").hide();

elAnswerButtons = document.querySelector('.CarouselAnswerButtonList');

var QuestionCount = document.querySelectorAll("div.CarouselCardFrame div[data-choiceid]").length;
var index = 0;

elAnswerButtons.addEventListener("pointerdown", () => {
index++;
});

elAnswerButtons.addEventListener("click", () => {
if(index == QuestionCount)
{
this.clickNextButton();
}
});
});

Badge +4

Thank you!! That worked perfectly

Badge +6

Hi @Tom_1842, Its not working for Multiselect Carousel. Could you please help to provide solution for multiselect Carousel question.

Userlevel 7
Badge +27

Hi @MadhurSingh  what the above code is doing is counting the number of times that the Carousel buttons are clicked and then advancing the page once the buttons are clicked the same number of times as there are cards.

This is workable for Single Selects (as long as the respondent is not going backwards to change answers to previous statements) since it advances the Card after each click, but not ideal for Multiple Selects because it is not known how many times the respondents will click the buttons for any statement before wanting to see the next one.

You could create JS that uses getChoiceValue to check if each statement has received at least 1 answer and then show the Next Button once each statement has received an answer. To give it a try, create a Matrix question with 5 statements that is set to Multiple Answer and to Carousel. Then, add the below to the question's JavaScript in the OnReady section:

//hide next button
jQuery("#NextButton").hide();

//define buttons to click
var elAnswerButtons = document.querySelector('.CarouselAnswerButtonList');

//function to check selections
var that = this;
var selections = 0;
function checkSelections() {

var sel1 = that.getChoiceValue(1);
var sel2 = that.getChoiceValue(2);
var sel3 = that.getChoiceValue(3);
var sel4 = that.getChoiceValue(4);
var sel5 = that.getChoiceValue(5);
selections = sel1+sel2+sel3+sel4+sel5;
console.log(selections);

//if false exists, keep hiding next button
//if false does not exist, all statements answered, show next button
if (selections.indexOf("false") !== -1) {
jQuery("#NextButton").hide();
} else {
jQuery("#NextButton").show();
}

}

var myTimeout;

//wait 5ms so that getChoiceValue can return values
elAnswerButtons.addEventListener("click", () => {
myTimeout = setTimeout(checkSelections, 5)
});

//clear Timeout on nextclick
jQuery("#NextButton").on('click', () => {
clearTimeout(myTimeout);
});

 

Looking back, I think this is also how I would approach the Single Answer auto-advancing after all the Statements have been answered. Create a Matrix question with 5 statements that is set to Single Answer and Carousel. Then use the below JS:

//hide next button
jQuery("#NextButton").hide();

//define buttons to click
var elAnswerButtons = document.querySelector('.CarouselAnswerButtonList');

//function to check selections
var that = this;
var selections = 0;
function checkSelections() {

var sel1 = that.getChoiceValue(1);
var sel2 = that.getChoiceValue(2);
var sel3 = that.getChoiceValue(3);
var sel4 = that.getChoiceValue(4);
var sel5 = that.getChoiceValue(5);
selections = sel1+sel2+sel3+sel4+sel5;
console.log(selections);

//if false exists, keep hiding next button
//if false does not exist, all statements answered, click Next Button
if (selections.indexOf("false") !== -1) {
jQuery("#NextButton").hide();
} else {
that.clickNextButton();
}

}

var myTimeout;

//wait 5ms so that getChoiceValue can return values
elAnswerButtons.addEventListener("click", () => {
myTimeout = setTimeout(checkSelections, 5)
});

//clear Timeout on nextclick
jQuery("#NextButton").on('click', () => {
clearTimeout(myTimeout);
});

 

Badge +6

@Tom_1842. Thank you so much for your reply, but unfortunately its not working.

Userlevel 7
Badge +33

@Tom_1842. Thank you so much for your reply, but unfortunately its not working.

 @MadhurSingh , I just respondent to your post. Refer this.

Userlevel 7
Badge +27

@MadhurSingh I see in the other thread that your use case is a little different than what my code was doing. The code I posted is about when all Carousel statements receive an answer, while the code you were looking for has to do with when the last statement is displayed to the respondent. I am glad you got helped! For anyone else, it doesn't look like the code I posted is compatible with Carry Forward, but the below revision that checks if the number of selected choices is equal to the number of available choices is working on my end if interested in hiding the next button until all Carousel statements receive an answer.

//hide next button
jQuery("#NextButton").hide();

//define buttons to click
var elAnswerButtons = document.querySelector('.CarouselAnswerButtonList');

//function to check selections
var that = this;
var selections = 0;
function checkSelections() {

var allchoices = that.getChoices().length;
var allselchoices = that.getSelectedChoices().length;
console.log(allchoices);
console.log(allselchoices);

//if lengths match, show next button
//if not, hide next button
if (allchoices == allselchoices) {
jQuery("#NextButton").show();
} else {
jQuery("#NextButton").hide();
}

}

var myTimeout;

//wait 5ms so that getChoices can return values
elAnswerButtons.addEventListener("click", () => {
myTimeout = setTimeout(checkSelections, 5)
});

//clear Timeout on nextclick
jQuery("#NextButton").on('click', () => {
clearTimeout(myTimeout);
});


 

Badge +6

@Tom_1842 : Thank you so much for your quick response.

Leave a Reply