@@ -45,136 +45,142 @@ where respondents are expected to fill in the first two columns and check the th
4545That is, the respondent should fill in EITHER the first two columns OR check the third column.
4646The JavaScript code below is used to ensure that if the third column is checked, the first two columns are hidden and cleared.
4747It also ensures that at least one of the first two columns is filled in or that the third column is checked before allowing the survey next button to be clicked to advance the survey to the next page.
48+ It allows for this advancement logic to function for three blocks of absolute frequency questions on the same page.
49+ If you would like to add additional blocks, identify their QID by clicking Preview in Qualtrics, right clicking, and selecting "Inspect".
50+ Then click on the survey block you want to identify and enter the QID where it says "add any additional blocks here" below.
4851
4952``` javascript
5053Qualtrics .SurveyEngine .addOnReady (function () {
51- console .log (" ✅ JS is running!" );
54+ console .log (" ✅ SBS script running for" , this .questionId );
55+
56+ /* ------------------------------------------------------------------
57+ 1. === per-row formatting + checkbox-hide logic ===
58+ ------------------------------------------------------------------ */
5259
5360 var matrix = this .getQuestionContainer ();
54- var rows = matrix .querySelectorAll (" tr.Choice" );
61+ var rows = matrix .querySelectorAll (" tr.Choice" );
62+ var that = this ;
5563
56- // Widen the header (leftmost) column
5764 var headerCell = matrix .querySelector (" th" );
58- if (headerCell) {
59- headerCell .style .width = " 300px" ;
60- }
65+ if (headerCell) headerCell .style .width = " 300px" ;
6166
6267 for (var r = 0 ; r < rows .length ; r++ ) {
63- (function (row , rowIndex ) {
68+ (function (row ) {
6469 var cells = row .querySelectorAll (" td" );
6570 if (cells .length < 9 ) return ;
6671
67- var textCell = cells[2 ]; // SBS1: text input
68- var dropdownCell = cells[5 ]; // SBS2: dropdown
69- var checkboxCell = cells[8 ]; // SBS3: checkbox
70- var checkbox = checkboxCell .querySelector (' input[type=" checkbox"] ' );
71- var inputField = textCell .querySelector (' input[type=" text"] ' );
72- var dropdownField = dropdownCell .querySelector (' select' );
72+ var textCell = cells[2 ]; // text input
73+ var dropdownCell = cells[5 ]; // dropdown
74+ var checkboxCell = cells[8 ]; // checkbox
75+ var checkbox = checkboxCell .querySelector (" input[type=' checkbox'] " );
76+ var inputField = textCell .querySelector (" input[type=' text'] " );
77+ var dropdownFld = dropdownCell .querySelector (" select" );
7378
74- // Widen the leftmost item label column (first td)
7579 var labelCell = row .querySelector (" td:first-child" );
76- if (labelCell) {
77- labelCell .style .width = " 300px" ;
78- }
80+ if (labelCell) labelCell .style .width = " 300px" ;
7981
80- // Set column widths
81- textCell .style .width = " 100px" ; // Smaller input box
82- dropdownCell .style .width = " 160px" ; // Wider dropdown
83- checkboxCell .style .width = " 120px" ; // Checkbox column wider
82+ textCell .style .width = " 100px" ;
83+ dropdownCell .style .width = " 160px" ;
84+ checkboxCell .style .width = " 120px" ;
8485 checkboxCell .style .textAlign = " center" ;
85-
86- // Prevent input cells from adding extra borders
87- textCell .style .borderRight = " none" ;
86+ textCell .style .borderRight = " none" ;
8887 dropdownCell .style .borderRight = " none" ;
8988
90- // Format separators
9189 for (var i = 0 ; i < cells .length ; i++ ) {
92- if (cells[i].className .indexOf (" Separator1 " ) !== - 1 || cells[i]. className . indexOf ( " Separator2 " ) !== - 1 ) {
93- cells[i].style .display = " " ;
94- cells[i].style .minWidth = " 1px" ;
95- cells[i].style .width = " 1px" ;
90+ if (cells[i].className .indexOf (" Separator " ) !== - 1 ) {
91+ cells[i].style .display = " " ;
92+ cells[i].style .minWidth = " 1px" ;
93+ cells[i].style .width = " 1px" ;
9694 cells[i].style .borderRight = " 1px solid #ccc" ;
97- cells[i].style .borderLeft = " none" ;
98- if (cells[i].innerHTML .trim () === " " ) {
99- cells[i].innerHTML = " " ;
100- }
95+ cells[i].style .borderLeft = " none" ;
96+ if (cells[i].innerHTML .trim () === " " ) cells[i].innerHTML = " " ;
10197 }
10298 }
10399
100+ // -- initial hide if checkbox pre-checked
104101 if (checkbox && checkbox .checked ) {
105- textCell .style .visibility = " hidden" ;
102+ textCell .style .visibility = " hidden" ;
106103 dropdownCell .style .visibility = " hidden" ;
107- textCell .style .border = " none" ;
108- dropdownCell .style .border = " none" ;
109- if (inputField) inputField .value = " " ;
110- if (dropdownField) dropdownField .selectedIndex = 0 ;
104+ textCell .style .border = " none" ;
105+ dropdownCell .style .border = " none" ;
106+ if (inputField) inputField .value = " " ;
107+ if (dropdownFld) dropdownFld .selectedIndex = 0 ;
111108 }
112109
110+ // -- live hide/show when checkbox toggled
113111 if (checkbox) {
114112 checkbox .addEventListener (" change" , function () {
115113 var hide = checkbox .checked ;
116114
117- textCell .style .visibility = hide ? " hidden" : " visible" ;
115+ textCell .style .visibility = hide ? " hidden" : " visible" ;
118116 dropdownCell .style .visibility = hide ? " hidden" : " visible" ;
119- textCell .style .border = hide ? " none" : " " ;
120- dropdownCell .style .border = hide ? " none" : " " ;
117+ textCell .style .border = hide ? " none" : " " ;
118+ dropdownCell .style .border = hide ? " none" : " " ;
121119
122120 if (hide) {
123- if (inputField) inputField .value = " " ;
124- if (dropdownField) dropdownField .selectedIndex = 0 ;
125- }
126-
127- for (var i = 0 ; i < cells .length ; i++ ) {
128- if (cells[i].className .indexOf (" Separator1" ) !== - 1 || cells[i].className .indexOf (" Separator2" ) !== - 1 ) {
129- cells[i].style .display = " " ;
130- cells[i].style .minWidth = " 1px" ;
131- cells[i].style .width = " 1px" ;
132- cells[i].style .borderRight = " 1px solid #ccc" ;
133- cells[i].style .borderLeft = " none" ;
134- if (cells[i].innerHTML .trim () === " " ) {
135- cells[i].innerHTML = " " ;
136- }
137- }
121+ if (inputField) inputField .value = " " ;
122+ if (dropdownFld) dropdownFld .selectedIndex = 0 ;
138123 }
139124 });
140125 }
141- })(rows[r], r);
126+ })(rows[r]);
127+ }
128+
129+ /* ------------------------------------------------------------------
130+ 2. === global “all 3 blocks must be complete” validation =========
131+ ------------------------------------------------------------------ */
132+
133+ /* create the shared tracker once (first time any block loads) */
134+ if (! window .SBSCompletion ) {
135+ window .SBSCompletion = { " QID1" : false , " QID10" : false , " QID12" : false };
142136 }
143137
144- this .disableNextButton ();
145- var that = this ;
138+ function rowIsValid (cells ) {
139+ var textInput = cells[2 ].querySelector (" input[type='text']" );
140+ var dropdown = cells[5 ].querySelector (" select" );
141+ var checkbox = cells[8 ].querySelector (" input[type='checkbox']" );
142+
143+ var inputHas = textInput && textInput .value .trim () !== " " ;
144+ var dropHas = dropdown && dropdown .value .trim () !== " " ;
145+ var checkHas = checkbox && checkbox .checked ;
146146
147- function validateRows () {
148- var allValid = true ;
147+ return (inputHas && dropHas) || checkHas;
148+ }
149149
150+ function validateThisBlock () {
151+ var everyRowValid = true ;
150152 for (var r = 0 ; r < rows .length ; r++ ) {
151153 var cells = rows[r].querySelectorAll (" td" );
152154 if (cells .length < 9 ) continue ;
155+ if (! rowIsValid (cells)) { everyRowValid = false ; }
156+ }
153157
154- var textInput = cells[2 ].querySelector (' input[type="text"]' );
155- var dropdown = cells[5 ].querySelector (' select' );
156- var checkbox = cells[8 ].querySelector (' input[type="checkbox"]' );
157-
158- var inputHasValue = textInput && textInput .value .trim () !== " " ;
159- var dropdownHasValue = dropdown && dropdown .value .trim () !== " " ;
160- var checkboxChecked = checkbox && checkbox .checked ;
161-
162- var rowIsValid = (inputHasValue && dropdownHasValue) || checkboxChecked;
158+ /* store this block’s status */
159+ window .SBSCompletion [that .questionId ] = everyRowValid;
163160
164- if ( ! rowIsValid) {
165- allValid = false ;
166- }
167- }
161+ /* check all three blocks */
162+ var allDone = window . SBSCompletion [ " QID1 " ] &&
163+ window . SBSCompletion [ " QID10 " ] &&
164+ window . SBSCompletion [ " QID12 " ]; /* add any additional blocks here */
168165
169- if (allValid) {
170- that .enableNextButton ();
171- } else {
172- that .disableNextButton ();
173- }
166+ if (allDone) { that .enableNextButton (); }
167+ else { that .disableNextButton (); }
174168 }
175169
176- validateRows ();
177- matrix .addEventListener (" input" , validateRows);
178- matrix .addEventListener (" change" , validateRows);
170+ /* disable Next on first load */
171+ that .disableNextButton ();
172+
173+ /* attach listeners */
174+ setTimeout (validateThisBlock, 300 );
175+ matrix .addEventListener (" input" , validateThisBlock);
176+ matrix .addEventListener (" change" , validateThisBlock);
177+
178+ // set up interval for rechecking validation; this re-enables the script in case Qualtrics overrides it after some time interval
179+ var validationInterval = setInterval (validateThisBlock, 5000 );
180+
181+ // clean up interval on page unload
182+ Qualtrics .SurveyEngine .addOnUnload (function () {
183+ clearInterval (validationInterval);
184+ });
179185});
180186```
0 commit comments