An Introduction to Custom Question Verification
Lighthouse Studio questions like multi-select, constant sum, etc. typically contain some flexibility in specifying proper answers to those questions. For example, you can specify a minimum and maximum number of checkboxes, or an amount above or below a constant sum total to let an answer through even the respondent didn’t quite get their math correct.
You can also enter script into most of these settings. For example, if I first asked a respondent “How many hours do you spend on social media in a week?” and then want to ask them how much of that is during the weekend, I can enter the first question name as the maximum value allowed in the second question:
But, sometimes things get complicated and you need a bit more control over your answer validation. In these cases, it’s possible to write custom verification in JavaScript and leverage some tools we’ve made to make the process a little easier.
Why JavaScript?
JavaScript is typically the go-to language for doing things in the moment in web browsers, so it naturally allows us to write true/false statements that can be evaluated after the survey page has loaded and is sitting on the respondent’s screen without a constant back and forth with the web server Plus, there are tons of examples and tutorials freely available all across the internet to help along the way. Just remember that JavaScript can be viewed and modified by a tech-savvy respondent.
Example 1
This example is a bit silly in practice but will give us a great starting point to introduce custom verification. The task is to take a numeric question called Q1 and allow any value between 0 and 100, but prevent the user from entering 42.
The 0 and 100 end points are handled inherently by the Min and Max settings on a numeric question, but preventing 42 will require a custom check to see what exactly was entered in the box by the respondent.
Custom verification is found by clicking on the Advanced tab of a question on the bottom left and then move to the Custom JavaScript Verification tab:
The dropdown box allows you to choose if your custom verification should run before or after any system-level verification (like the Min Max setting, that the question is required, etc.). Generally it’s better to have custom verification run after system verifications so that we can deal with weird things (such as the respondent entering text where only numbers are allowed) and you can stick to the specific task of preventing the number 42.
To actually write the verification, we’ll leverage a function called SSI_GetValue() that allows us to pull the value of any variables currently on the page. This function has some things behind the scenes so you don’t have to worry as much about if you are looking at the value of a checkbox, numeric input, text input, etc.
If we name our numeric question Not42, then we can pull the value and place it into a true/false comparison like this:
if (SSI_GetValue('Not42') == 42)
{
strErrorMessage = '42 is not an allowed answer.';
}
We won’t get into all the details of JavaScript syntax here, but the basics of what we are doing is
if (logic test goes here)
{
do something here;
}
For custom question verification, the “do something here” is usually assigning a value to a custom error message variable we call strErrorMessage. If strErrorMessage gets assigned a value, then the page will display the error text (42 is not an allowed answer) as an error message in red to the respondent. If the statement is false, strErrorMessage does not get assigned any value and the page will submit (assuming no other system level error messages, like entering text instead of a number, will prevent moving forward in the survey).
“text” and ‘text’ <- these are Microsoft’s stylized punctuation
"text" 'text' <- these were copied and pasted from Notepad, which has no style/format capabilities
In part 2, we’ll dive into several additional examples of custom verification.
Part 2
In part 1 we introduced the idea of custom question verification by writing logical statements (true/false tests) using JavaScript. In part 2, we’re going to show several different examples to provide starting points to help you write your own custom verification.
The first thing to talk about, though, is good habits. In part 1 we showed a simple example:
if(SSI_GetValue('Not42') == 42)
{
strErrorMessage = '42 is not an allowed answer.';
}
This is a fine example of custom verification, but there are some good habits that will you help if you develop them sooner rather than later. Here’s a rewrite of the example above with a little more clarity should someone else have to take over your survey:
var answer = SSI_GetValue('Not42');
if (answer == 42)
{
strErrorMessage = '42 is not an allowed answer.';
}
This adds a few extra steps, but is generally a lot easier to review later on, or copy and paste to re-use over and over. The first is to introduce the idea of assigning survey variables to my own variables. Here we create a variable called “answer” and assign it the value of whatever Not42 is. Then we write a very legible logic test to see if the answer is 42. We also do a little bit of formatting and indenting to make what we’re doing easier to read.
For simple custom verification like this, it’s a bit overkill. But it’s an excellent habit to establish early on that will make more difficult custom verifications easier later on.
Example 1 – Compare two values on the same page
In this example we want to have one numeric box on the page asking for the respondent’s age, and then a second box asking for their child’s age. The built-in verification will handle the ranges of both boxes, but we need a little custom verification to ensure the respondent age is greater than the child age. With two numeric questions on the same page called Q1 (respondent age) and Q2 (child age), we could write some quick verification like this:
if(SSI_GetValue('Q1) <= SSI_GetValue('Q2'))
{
strErrorMessage = 'Your age must be greater than your child age.';
}
Again, with a little more effort, we can make this much more readable:
var RespAge = SSI_GetValue('Q1');
var ChildAge = SSI_GetValue('Q2');
if (RespAge <= ChildAge)
{
strErrorMessage = 'Your age must be greater than your child age.';
}
You can use greater than (>), greater than or equal to (>=), less than (<), less than or equal to (<=), equal to (==), and not equal to (!=) in your logical tests. Remember a single = is for assigning values to variables, not logical tests.
Example 2
For this example, we’ll have three numeric questions on the page (Q6, Q7, Q8) that all can range between 0-100, but we want to prevent all options from being less than 50. We’ll skip straight to the easier to read code here:
var FirstResponse = SSI_GetValue('Q6');
var SecondResponse = SSI_GetValue('Q7');
var ThirdResponse = SSI_GetValue('Q8');
if(FirstResponse < 50 && SecondResponse < 50 && ThirdResponse < 50)
{
strErrorMessage = 'At least one response must be 50 or greater.';
}
The && operator lets us string together multiple logic tests so that the whole statement must be true (test1 AND test2 AND test3). Two vertical lines, ||, is used for the “or” operator.
Example 3
In this example we’ll add a new issue where we want to check that the value of something on the current page (Q5) is higher than the value on a different page in the survey (Q1). Our reliable friend SSI_GetValue() will not work for both this time since the second question is not currently on the page where the verification runs. Remember that JavaScript is for interacting with things live on the page. To pull this off, will we mix traditional scripting with custom JavaScript like this (the verification is being run on the page that contains Q5):
var FirstAnswer = [% Q1 %];
var SecondAnswer = SSI_GetValue(‘Q5’);
if (SecondAnswer > FirstAnswer)
{
strErrorMessage = ‘Your answer is too high. It must be less than or equal to [%Q1%]’;
}
So what’s going on here? Traditional scripting tags are used all over to reference values to other questions, and they work just as well when writing any type of custom code. Think of it as an “order of operations” where when the page is being built for the respondent, [% Q1 %] is going to get filled in from the database up on the server first, and then the JavaScript will run after the page is sent to the respondent’s browser. So if the respondent entered a value of 10 for Q1, when the page containing Q5 is loaded up, the JavaScript above actually is
var FirstAnswer = 10;
var SecondAnswer = SSI_GetValue('Q5');
if (SecondAnswer > FirstAnswer)
{
strErrorMessage = 'Your answer is too high. It must be less than or equal to 10';
}
This is a similar concept from part 1 where we can take the answer to a question and enter it inside scripting tags for any built-in verification.
When the survey is running, [%SocialMediaWk%] will evaluate to a number when the SocialMediaDay question is shown to the respondent. With this type of scripting, though, SocialMediaWk would need to be answered and sitting in the survey database before it could be used to validate SocialMediaDay. If both questions were on the same page, SocialMediaWk is empty!
Another clever trick to utilize this is to remember you can use the QuestionName() script to automatically return whatever the current question name is where your custom verification is running. If we wanted to repeatedly check different questions against the value of Q1, we could make our verification easier to copy and paste like this:
var FirstAnswer = [% Q1 %];
var SecondAnswer = SSI_GetValue('[%QuestionName()%]');
if (SecondAnswer > FirstAnswer)
{
strErrorMessage = 'Your answer is too high. It must be less than or equal to [%Q1%].';
}
[% QuestionName() %] will automatically fill in with whatever the current question name is where the verification is running, making it very easy to copy and paste over and over.
In Part 3 we’ll continue on with more concepts and examples.
Part 3
In this last part we’re going to talk about numbers versus text and get a little bit further into efficiency with looping.
Previously we have been using numeric examples since they will be the most common. Comparing text is a little less common, but our first example will help us understand a valuable point. Text is typically called a string (a string of characters put together) in programming languages.
Example 1
If we wanted to compare the open-end text of a question on one page (Q1) with the open-end text of a question on a different page (Q5) and give an error if they are the same, we would do that like this:
var FirstResponse = '[% Q11 %]';
var SecondResponse = SSI_GetValue('Q12');
if (FirstResponse == SecondResponse)
{
strErrorMessage = 'Your response needs to be different than the previous question.';
}
JavaScript needs to know if you are dealing with numbers or strings, so when declaring the variable FirstResponse, because we are doing text comparisons, we need to wrap whatever Q1 evaluates to inside either single or double quotes.
A more simple example would be to consider the following code snippits:
var x = 'hello there';
var y = x;
var x = 'hello there';
var y = 'x';
In the first two lines, we create a variable called x and assign it the string hello there as its value. We then create a second variable called y and assign it to be the same as x: hello there.
In the second example, we do the same in line one, but in line two when we create variable y but because we use 'x', variable y has a value that is just the character x as a string.
As a final note in this example, you can use single or double quotes within each other so long as you maintain consistency when beginning and ending the variable. Here are a few examples that are all valid when assigning the custom error message to the strErrorMessage variable:
strErrorMessage = 'You cannot enter that as a value.';
strErrorMessage = "You cannot enter that as a value.";
strErrorMessage = 'You cannot enter "that" as a value.';
strErrorMessage = "You can't enter that as a value.";
Here’s a few examples that illustrate how JavaScript behaves if you end up combining numbers and text:
var x = 16 + 4 + 'Q5'
Results in x having a value of 20Q5 because the two numbers can be added, but adding the string turns the whole thing into a string. var x = 'Q5' + 16 + 4
Results in x having a value of Q5164 because the string came first.
Example 2
In example 2, let’s say we had a grid question (Q55) with rows of numeric fields and we want to check to see if any box has a value less than 10 (ignore for a moment that we could do this easily by setting the minimum value of each box to 10!). We could brute force our way through with single if statements like this:
if(SSI_GetValue('Q55_r1_c1') < 10)
{
strErrorMessage = 'All answers must be greater than or equal to 10';
if(SSI_GetValue('Q55_r2_c1') < 10)
{
strErrorMessage = 'All answers must be greater than or equal to 10';
}
if(SSI_GetValue('Q55_r3_c1') < 10)
{
strErrorMessage = 'All answers must be greater than or equal to 10';
}
}
And so on, but this is pretty tedious if we have a lot of rows. It’s much more efficient (especially if we need to change the name of the question later on) to get into looping. There are lots of examples all across the internet about JavaScript looping, so we’ll just dive into the code here. The key aspect to this is that every variable on the page is going to follow the naming of Q55_r#_c1. That lets us set up a loop that will go through, say, 5 rows like this:
for (var i = 1; i <= 5; i++)
{
var CurrentBoxName = 'Q55_r' + i + '_c1';
var CurrentBoxValue = SSI_GetValue(CurrentBoxName);
if (CurrentBoxValue < 10)
{
strErrorMessage = 'All answers must be 10 or greater.';
}
}
The key concept here is that we start a loop with a variable i set to 1. Each iteration, we increment i for as long as i is less than or equal to 5. Then inside the loop, we append the value of i to the base question name Q1_r so that we start off with Q1_r1_c1, then Q1_r2_c1, then Q1_r3_c1 up through Q1_r5_c1.
We could get a little fancier by putting the value of i into our error message as well like this:
for (var i = 1; i <= 5; i++)
{
var CurrentBoxName = 'Q55_r' + i + '_c1';
var CurrentBoxValue = SSI_GetValue(CurrentBoxName);
if (CurrentBoxValue < 10)
{
strErrorMessage = 'All answers must be 10 or greater. Please check row ' + i ;
}
}
If we had a grid question with 3 rows and 2 columns, then we would do a loop within a loop in order to start at row 1 and check columns 1-2, then move to row 2 and check columns 1-2, and so on like this:
for (var i =1; i <= 3; i++) // This loops 1-3 for the row number
{
for (var j = 1; j <= 2; j++) // This loops 1-2 for the column number
{
var CurrentBoxName = 'Q111_r' + i + '_c' + j; // Builds the name Q1_r#_C#
var CurrentBoxValue = SSI_GetValue(CurrentBoxName);
if (CurrentBoxValue < 10)
{
strErrorMessage = 'All answers must be 10 or greater. Please check row ' + i + ' column ' + j + '.';
}
}
}
Note we’ve also added some comments here, another great habit to get in to if you are write complex logic. Your future self will appreciate the notes left by your past when you need to revisit your code days or weeks later.
Breaking out of loops
And just to introduce another programming concept, with the way this is written, the loop would go through in its entirety before the page will refresh with the error message. This means if the user entered an invalid value in the first and the last box, strErrorMessage would first be assigned to say “Please check row 1 column 1” but then it would be overwritten with “Please check row 3 column 2” because the loop continues until it is done. If you wanted to stop going through the loop at the first instance of a problem, we can tweak the conditional logic of the loop running by going from “run this loop as long the iteration value is less than N” to “run this loop as long as the iteration variable is less than N and strErrorMessage is still empty” like this:
for (var i=1; i<=3 && strErrorMessage==""; i++) // This loops 1-3 for the row number
{
for (var j=1; j<=2 && strErrorMessage==""; j++) // This loops 1-2 for the column number
{
var CurrentBoxName = 'Q111_r' + i + '_c' + j; // Builds the name Q1_r#_C#
var CurrentBoxValue = SSI_GetValue(CurrentBoxName);
if (CurrentBoxValue < 10)
{
strErrorMessage = 'All answers must be 10 or greater. Please check row ' + i + ' column ' + j + '.' ;
}
}
}
If we only had a single loop going, we could manually insert a “break” command when we assign strErrorMessage like this:
{
strErrorMessage = “
Error message here.”;
break;
}
Example 3
Our last example will be about troubleshooting custom verification. Because JavaScript runs behind the scenes, it can be difficult to know what exactly is broken if your custom verification isn’t doing what you want it to do. To help with that, we can use alerts that will pop up messages to us as our code executes. Here’s an example where we insert alerts to basically step through our code line by line. Q5 and Q6 are on the same page and we are trying to write verification if their sum is greater than 100.
var FirstResponse = SSI_GetValue('Q51');
alert('Variable FirstResponse value is ' + FirstResponse);
var SecondResponse = SSI_GetValue('Q52');
alert('Variable SecondResponse value is ' + SecondResponse);
var Sum = FirstResponse + SecondResponse;
alert('The sum of the two is ' + Sum);
strErrorMessage = 'You hit the error';
Here we don’t even need to fully specify an if/then value for strErrorMessage. By simply declaring it, I will see the three alerts and then the error will show to let me know I’ve hit the end of all of my code.
This is an important principle of troubleshooting. If you have line after line of code and it’s not working, it can often be much more efficient to strip out some complexity and just check to make sure the basics are working, then gradually add back in your complexity.
For the Future
Lighthouse Studio contains other JavaScript functions available to do even more advanced things on a page. If you’d like to read more, you can check out our Help documentation.