Skip to content

Commit dcfeda8

Browse files
committed
20250609 - fix qualtrics override bug
1 parent 31ff66b commit dcfeda8

File tree

1 file changed

+88
-82
lines changed

1 file changed

+88
-82
lines changed

qualtrics-manual.Rmd

Lines changed: 88 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -45,136 +45,142 @@ where respondents are expected to fill in the first two columns and check the th
4545
That is, the respondent should fill in EITHER the first two columns OR check the third column.
4646
The JavaScript code below is used to ensure that if the third column is checked, the first two columns are hidden and cleared.
4747
It 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
5053
Qualtrics.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 = "&nbsp;";
100-
}
95+
cells[i].style.borderLeft = "none";
96+
if (cells[i].innerHTML.trim() === "") cells[i].innerHTML = "&nbsp;";
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 = "&nbsp;";
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

Comments
 (0)