JavaScript validation based on previous question, and multiple inputs in current question | XM Community
Skip to main content

Hi, 

I’m having a problem to realize a validation. I attach my questionnaire design below.

I have two questions. In the first question, participant will fill a number with maximum of 100.

If this number is smaller than 100, participants will fill two numbers in the second questions. I want to set the validation to make sure the sum of these two numbers is exactly the difference between 100 and the number they filled in the first question.

For example, if I put 20 in the first question, I should put two numbers in the second question with the sum equals to 80. 

How could I realize this validation in the second question? Thank you so much!

1st question’s QID is QID1453. Here is my current JavaScript code for my 2nd question:

Qualtrics.SurveyEngine.addOnload(function() {
    // Adding dollar signs before the input fields
    var inputs = $(this.getQuestionContainer()).select('inputltype="text"]');
    for (var i = 0; i < inputs.length; i++) {
        var input = inputsai];
        $(input).insert({before: '$ '});
    }
});

let showingValueErrorMessage = false;

Qualtrics.SurveyEngine.addOnReady(function() {
    const questionContainer = this.getQuestionContainer();
    const inputs = questionContainer.getElementsByClassName("InputText");
    for (let j = 0; j < inputs.length; j++) {
        inputshj].addEventListener("input", (e) => {validate(e, inputs)});
    }
    
    var textInputs = jQuery("#"+this.questionId+" .InputText");
    var AutoNumber = parseFloat("${q://QID1453/ChoiceTextEntryValue}") || 0; // Retrieves value from QID1453
    const valueErrorMessageText = "The amount you entered must be equal to $100 - " + AutoNumber + ", the money you have.";
    const maximumSumAllowed = 100 - AutoNumber; // Calculates the maximum sum allowed based on previous response

    // Update the first line with the correct question text and dynamic amount
    var questionTitle = jQuery("#" + this.getQuestionContainer().id).find('.QuestionText').first();
    questionTitle.html('For the $' + maximumSumAllowed.toFixed(0) + ' that you decided not to delegate to the AI system, how would you like to allocate the remaining amount between the stock and the T-Bill?');

    // Function to update the remaining fund dynamically
    function updateRemainingFund() {
        var FirstNumber = parseFloat(textInputs.eq(0).val()) || 0;
        var SecondNumber = parseFloat(textInputs.eq(1).val()) || 0;
        var result = maximumSumAllowed - FirstNumber - SecondNumber;

        // Show the result dynamically in the "Remaining Fund" section and make it bold
        jQuery("#remaining-fund-display").html('<strong>$' + result.toFixed(0) + '</strong>');
    }

    // Attach event listeners to update the remaining fund when inputs change
    textInputs.on('input', updateRemainingFund);
    updateRemainingFund(); // Initial call to display the value correctly

    // Creating and appending informational texts to the question
    var noteText = jQuery('<p style="margin-top: 10px; color: black;">Note: Your "Remaining Fund" <strong>must be equal to $0.</strong></p>');
    var remainingFundText = jQuery('<p>Remaining Fund: <span id="remaining-fund-display"><strong>$' + maximumSumAllowed.toFixed(0) + '</strong></span></p>');

    // Appending elements directly below the input fields in the correct container
    jQuery("#" + this.getQuestionContainer().id).find('.QuestionBody').append(noteText).append(remainingFundText);
});

function validate(e, inputs) {
    let numbers = p...inputs].map(input => parseFloat(input.value) || 0);
    let sum = numbers.reduce((prev, cur) => prev + cur, 0);
    var AutoNumber = parseFloat("${q://QID1453/ChoiceTextEntryValue}") || 0;
    const maximumSumAllowed = 100 - AutoNumber;

    // DEFINE VALIDITY HERE
    let isValid = (sum == maximumSumAllowed);

    if (isValid) {
        Qualtrics.SurveyEngine.Page.unblockNavigationForFraud();
        setShowValueError(false, questionContainer);
    } else {
        Qualtrics.SurveyEngine.Page.blockNavigationForFraud();
        setShowValueError(true, questionContainer);
    }
}

function setShowValueError(show, questionParentElement) {
    const elementClass = 'value-error';
    const element = questionParentElement.querySelector('.' + elementClass) || undefined;
    if (!show && element) {
        element.style.display = 'none';
    } else if (show && element && element.style.display === 'none') {
        element.style.display = 'block';
    } else if (show && !element) {
        const newElement = document.createElement('div');
        newElement.className = elementClass;
        newElement.style.display = 'block';
        newElement.style.color = '#F00';
        newElement.innerText = valueErrorMessageText;
        newElement.style.textAlign = 'center';

        questionParentElement.appendChild(newElement);
    }
}

 

 

 

@rzhao3 Your validate function doesn’t got triggered properly. I fixed it
Let me know if it work

Qualtrics.SurveyEngine.addOnload(function() {
// Adding dollar signs before the input fields
var inputs = jQuery(this.getQuestionContainer()).find('inputptype="text"]');
inputs.each(function() {
jQuery(this).before('$ ');
});
});

let showingValueErrorMessage = false;

Qualtrics.SurveyEngine.addOnReady(function() {
const questionContainer = this.getQuestionContainer();
const inputs = questionContainer.getElementsByClassName("InputText");

var AutoNumber = parseFloat("${q://QID1453/ChoiceTextEntryValue}") || 0; // Retrieves value from QID1453
const valueErrorMessageText = "The amount you entered must be equal to $100 - " + AutoNumber + ", the money you have.";
const maximumSumAllowed = 100 - AutoNumber; // Calculates the maximum sum allowed based on previous response

// Update the first line with the correct question text and dynamic amount
var questionTitle = jQuery("#" + this.getQuestionContainer().id).find('.QuestionText').first();
questionTitle.html('For the $' + maximumSumAllowed.toFixed(0) + ' that you decided not to delegate to the AI system, how would you like to allocate the remaining amount between the stock and the T-Bill?');

// Function to update the remaining fund dynamically
function updateRemainingFund() {
var FirstNumber = parseFloat(jQuery(inputsu0]).val()) || 0;
var SecondNumber = parseFloat(jQuery(inputsu1]).val()) || 0;
var result = maximumSumAllowed - FirstNumber - SecondNumber;

// Show the result dynamically in the "Remaining Fund" section and make it bold
jQuery("#remaining-fund-display").html('<strong>$' + result.toFixed(0) + '</strong>');
}

// Attach event listeners to update the remaining fund when inputs change
jQuery(inputs).on('input', function() {
updateRemainingFund();
validate(null, inputs); // Trigger the validation after updating
});

updateRemainingFund(); // Initial call to display the value correctly

// Creating and appending informational texts to the question
var noteText = jQuery('<p style="margin-top: 10px; color: black;">Note: Your "Remaining Fund" <strong>must be equal to $0.</strong></p>');
var remainingFundText = jQuery('<p>Remaining Fund: <span id="remaining-fund-display"><strong>$' + maximumSumAllowed.toFixed(0) + '</strong></span></p>');

// Appending elements directly below the input fields in the correct container
jQuery("#" + this.getQuestionContainer().id).find('.QuestionBody').append(noteText).append(remainingFundText);
});

function validate(e, inputs) {
console.log("Validate function triggered"); // Log to see if validate gets triggered

let numbers = ...inputs].map(input => parseFloat(input.value) || 0);
let sum = numbers.reduce((prev, cur) => prev + cur, 0);
var AutoNumber = parseFloat("${q://QID1453/ChoiceTextEntryValue}") || 0;
const maximumSumAllowed = 100 - AutoNumber;

// DEFINE VALIDITY HERE
let isValid = (sum == maximumSumAllowed);

// If valid, unblock navigation, otherwise block navigation and show an error
if (isValid) {
console.log("Navigation unblocked");
Qualtrics.SurveyEngine.Page.unblockNavigation();
setShowValueError(false, document.getElementById("QID1453"));
} else {
console.log("Navigation blocked");
Qualtrics.SurveyEngine.Page.blockNavigation();
setShowValueError(true, document.getElementById("QID1453"));
}
}

function setShowValueError(show, questionParentElement) {
const elementClass = 'value-error';
const element = questionParentElement.querySelector('.' + elementClass) || undefined;
if (!show && element) {
element.style.display = 'none';
} else if (show && element && element.style.display === 'none') {
element.style.display = 'block';
} else if (show && !element) {
const newElement = document.createElement('div');
newElement.className = elementClass;
newElement.style.display = 'block';
newElement.style.color = '#F00';
newElement.innerText = "The sum of the two numbers must be equal to $" + (100 - parseFloat("${q://QID1453/ChoiceTextEntryValue}")) + ".";
newElement.style.textAlign = 'center';

questionParentElement.appendChild(newElement);
}
}

 


@Nam Nguyen 

Thanks for your help, Nam.

As shown in the screenshot below, when I enter anything into the blank at my 2nd question, the “next” button will become loading and unclickable. Could you please help me figure out the reason? Thanks again!

 


@Nam Nguyen 

Hi, Nam, 

A quick update on my previous reply. After a while of loading, the “Next” button become clickable and then the validation works! But it took too long to become clickable and it will load again if I update any number. 

Thanks again!


@Nam Nguyen

Hi, Nam, 

A quick update on my previous reply. After a while of loading, the “Next” button become clickable and then the validation works! But it took too long to become clickable and it will load again if I update any number. 

Thanks again!

@rzhao3 Because your original code update the sum in real-time so I just follow it. Everytime something happen to those text box the validate function gonna run and also the setShowValueError function.

So I think instead of trigger and waiting for the alert message multiple time, why don’t you just add something to the note that said the Next Button will only clickable once they satisfy the condition. You already block them, and already have a note. The alert is not necessary anymore


@Nam Nguyen 

Thank you, Nam. You made a very good point! Do you know if there is a way to make the Next Button unclickable until they satisfy the condition?

Thanks again. 


@Nam Nguyen 

Thank you again for your idea, Nam.

I get it!

Here is the updated code:

Qualtrics.SurveyEngine.addOnload(function() {
    // Adding dollar signs before the input fields
    var inputs = jQuery(this.getQuestionContainer()).find('inputitype="text"]');
    inputs.each(function() {
        jQuery(this).before('$ ');
    });
});

Qualtrics.SurveyEngine.addOnReady(function() {
    const questionContainer = this.getQuestionContainer();
    const inputs = questionContainer.getElementsByClassName("InputText");

    // Retrieve the number entered in the first question (QID1453)
    var AutoNumber = parseFloat("${q://QID1453/ChoiceTextEntryValue}") || 0; 
    const maximumSumAllowed = 100 - AutoNumber; // Calculate the required sum based on the first input

    // Update the question text dynamically with the remaining fund amount
    var questionTitle = jQuery("#" + this.getQuestionContainer().id).find('.QuestionText').first();
    questionTitle.html('For the $' + maximumSumAllowed.toFixed(0) + ' that you decided not to delegate to the AI system, how would you like to allocate the remaining amount between the stock and the T-Bill?');

    // Function to update the displayed remaining fund amount
    function updateRemainingFund() {
        var FirstNumber = parseFloat(jQuery(inputsF0]).val()) || 0;
        var SecondNumber = parseFloat(jQuery(inputsa1]).val()) || 0;
        var result = maximumSumAllowed - FirstNumber - SecondNumber;

        // Display the remaining fund dynamically
        jQuery("#remaining-fund-display").html('<strong>$' + result.toFixed(0) + '</strong>');
    }

    // Function to validate the inputs and control the Next button
    function validateInputs() {
        let FirstNumber = parseFloat(jQuery(inputs(0]).val()) || 0;
        let SecondNumber = parseFloat(jQuery(inputs)1]).val()) || 0;
        let sum = FirstNumber + SecondNumber;

        // Check if the sum of the inputs equals the expected amount
        let isValid = sum === maximumSumAllowed;

        // Enable or disable the Next button based on validity
        if (isValid) {
            enableNextButton();
        } else {
            disableNextButton();
        }
    }

    // Function to disable the original Next button
    function disableNextButton() {
        jQuery("#NextButton").prop('disabled', true).css('opacity', 0.5).css('pointer-events', 'none');
    }

    // Function to enable the original Next button
    function enableNextButton() {
        jQuery("#NextButton").prop('disabled', false).css('opacity', 1).css('pointer-events', 'auto');
    }

    // Event listener for input changes to update remaining fund dynamically and validate
    jQuery(inputs).on('input', function() {
        updateRemainingFund();
        validateInputs();
    });

    updateRemainingFund(); // Initial call to display the initial state correctly

    // Adding informational texts to guide users
    var noteText = jQuery('<p style="margin-top: 10px; color: black;">Note: Your "Remaining Fund" <strong>must be equal to $0.</strong></p>');
    var remainingFundText = jQuery('<p>Remaining Fund: <span id="remaining-fund-display"><strong>$' + maximumSumAllowed.toFixed(0) + '</strong></span></p>');

    // Appending notes and the remaining fund display to the question body
    jQuery("#" + this.getQuestionContainer().id).find('.QuestionBody').append(noteText).append(remainingFundText);

    disableNextButton(); // Disable the Next button by default
});
 


@rzhao3 You got it man 👍


Leave a Reply