I have a multiple choice question that uses loop and merge to display 100 message pairs. For each pair, one message is the English source message and the other is the machine translation of that message. I would like to display these messages next to eachother in the question text to make it easy for the respondents to compare the messages. However, when I use a table to display the messages next to eachother, the widths of the ‘message 1’ and ‘message 2’ columns vary (see screenshot below). Is there a way to make sure the widths of these two columns are always 50%?
In addition, I’m looking for a way to align the sentences in these columns. See the screenshot below for an example of what I mean by “sentence-aligned”. The sentences are separated by </br>’s in the loop and merge, but I can easily replace these separators by something else if needed.
Any help would be greatly appreciated!
Best answer by TomG
MargotF wrote:
TomG wrote:
Set the widths of the Message1 and Message2 cells to 50%.
If you add table rows and make each sentence a table cell (<td></td>) and set vertical-align to top they will align. Then you just need to style the cell borders (side borders only, then add top border to cells in top row and bottom border to cells in bottom row).
Another way would be to put every sentence in a <div></div> and either:
Scrap the table and use a grid instead
Set the minimum height of each div to match the max height of the tallest div in the row.
Thank you for your answer! I see how the two solutions you suggest would work with a regular question, but I haven’t been able to make them work with my loop & merge question, where the messages are piped text (see screenshot). I think both solutions you suggest would require a data structure like <sentence 1>, <translation sentence 1>, <sentence 2>, <translation sentence 2>, … I don’t think I can have a structure like that in a loop & merge question.
If you use the table approach, you could either create a loop & merge field for each sentence or use a delimiter between each sentence, split them with JS, and update the table cells.
If you use a grid or flex-boxes you need to put each sentence within a <div> and apply the correct CSS. If you use flex-boxes you’ll need to apply JS to set the heights to align.
Set the widths of the Message1 and Message2 cells to 50%.
If you add table rows and make each sentence a table cell (<td></td>) and set vertical-align to top they will align. Then you just need to style the cell borders (side borders only, then add top border to cells in top row and bottom border to cells in bottom row).
Another way would be to put every sentence in a <div></div> and either:
Scrap the table and use a grid instead
Set the minimum height of each div to match the max height of the tallest div in the row.
Set the widths of the Message1 and Message2 cells to 50%.
If you add table rows and make each sentence a table cell (<td></td>) and set vertical-align to top they will align. Then you just need to style the cell borders (side borders only, then add top border to cells in top row and bottom border to cells in bottom row).
Another way would be to put every sentence in a <div></div> and either:
Scrap the table and use a grid instead
Set the minimum height of each div to match the max height of the tallest div in the row.
Thank you for your answer! I see how the two solutions you suggest would work with a regular question, but I haven’t been able to make them work with my loop & merge question, where the messages are piped text (see screenshot). I think both solutions you suggest would require a data structure like <sentence 1>, <translation sentence 1>, <sentence 2>, <translation sentence 2>, … I don’t think I can have a structure like that in a loop & merge question.
Set the widths of the Message1 and Message2 cells to 50%.
If you add table rows and make each sentence a table cell (<td></td>) and set vertical-align to top they will align. Then you just need to style the cell borders (side borders only, then add top border to cells in top row and bottom border to cells in bottom row).
Another way would be to put every sentence in a <div></div> and either:
Scrap the table and use a grid instead
Set the minimum height of each div to match the max height of the tallest div in the row.
Thank you for your answer! I see how the two solutions you suggest would work with a regular question, but I haven’t been able to make them work with my loop & merge question, where the messages are piped text (see screenshot). I think both solutions you suggest would require a data structure like <sentence 1>, <translation sentence 1>, <sentence 2>, <translation sentence 2>, … I don’t think I can have a structure like that in a loop & merge question.
If you use the table approach, you could either create a loop & merge field for each sentence or use a delimiter between each sentence, split them with JS, and update the table cells.
If you use a grid or flex-boxes you need to put each sentence within a <div> and apply the correct CSS. If you use flex-boxes you’ll need to apply JS to set the heights to align.
Set the widths of the Message1 and Message2 cells to 50%.
If you add table rows and make each sentence a table cell (<td></td>) and set vertical-align to top they will align. Then you just need to style the cell borders (side borders only, then add top border to cells in top row and bottom border to cells in bottom row).
Another way would be to put every sentence in a <div></div> and either:
Scrap the table and use a grid instead
Set the minimum height of each div to match the max height of the tallest div in the row.
Thank you for your answer! I see how the two solutions you suggest would work with a regular question, but I haven’t been able to make them work with my loop & merge question, where the messages are piped text (see screenshot). I think both solutions you suggest would require a data structure like <sentence 1>, <translation sentence 1>, <sentence 2>, <translation sentence 2>, … I don’t think I can have a structure like that in a loop & merge question.
If you use the table approach, you could either create a loop & merge field for each sentence or use a delimiter between each sentence, split them with JS, and update the table cells.
If you use a grid or flex-boxes you need to put each sentence within a <div> and apply the correct CSS. If you use flex-boxes you’ll need to apply JS to set the heights to align.
I did some more experimenting and managed to get one of your proposed solutions to work (the one with the delimiter). Thank you!
@TomG The solution with the delimiter seems to work really nicely with LTR languages, but I also have some messages in RTL languages. As soon as I wrap the sentences in these RTL messages in some kind of tag (a <div> or a custom tag so I can specify the direction in a style attribute), the sentences end up below the table. For example, if I put something like this in the loop & merge:
I get a question that looks like this:
This is the JavaScript I’m currently using to create the table (vibe-coded though, as I do not have any JavaScript skills of my own):
Qualtrics.SurveyEngine.addOnReady(function() {
setTimeout(function() {// Delay to allow Qualtrics to replace piped text
document.querySelectorAll(".QuestionText").forEach(container => {
let hiddenTexts = container.getElementsByClassName("hidden-text");
if (hiddenTexts.length >= 2) {
let str1 = hiddenTexts[0].innerText.trim();
let str2 = hiddenTexts[1].innerText.trim();
functioncreateTable(str1, str2) {let messages1 = str1.split("$").filter(msg => msg.trim() !== "");
let messages2 = str2.split("$").filter(msg => msg.trim() !== "");
// Ensure both arrays have equal length by filling shorter one with empty stringslet maxLength = Math.max(messages1.length, messages2.length);
messages1 = [...messages1, ...Array(maxLength - messages1.length).fill("")];
messages2 = [...messages2, ...Array(maxLength - messages2.length).fill("")];
// Create tableconst table = document.createElement("table");
table.style.width = "100%";
table.style.borderCollapse = "collapse";
table.style.border = "2px solid black"; // Outer border only
table.style.marginTop = "15px";
// Create header rowconst headerRow = document.createElement("tr");
["Message 1", "Message 2"].forEach(headerText => {
const th = document.createElement("th");
th.textContent = headerText;
th.style.width = "50%"; // Ensure both columns are equal width
th.style.padding = "8px";
th.style.textAlign = "center"; // Center the header text
th.style.verticalAlign = "top"; // Align text at the top
th.style.backgroundColor = "#e0e0e0"; // Slightly darker gray
th.style.borderLeft = "2px solid black"; // Vertical border
th.style.borderRight = "2px solid black"; // Vertical border
headerRow.appendChild(th);
});
table.appendChild(headerRow);
// Populate table rowsfor (let i = 0; i < maxLength; i++) {
if (messages1[i] === "" && messages2[i] === "") continue; // Skip empty rowsconst row = document.createElement("tr");
[messages1[i], messages2[i]].forEach(text => {
const td = document.createElement("td");
td.textContent = text;
td.style.width = "50%"; // Ensure both columns are equal width
td.style.padding = "8px";
td.style.borderLeft = "2px solid black"; // Vertical border
td.style.borderRight = "2px solid black"; // Vertical border
td.style.verticalAlign = "top"; // Align text at the top
row.appendChild(td);
});
table.appendChild(row);
}
return table;
}
// Insert the table before the first hidden-text elementconst table = createTable(str1, str2);
hiddenTexts[0].parentNode.insertBefore(table, hiddenTexts[0]);
// Remove all hidden text elements for this questionArray.from(hiddenTexts).forEach(elem => elem.remove());
}
});
}, 250);
});