Have an idea?

Visit Sawtooth Software Feedback to share your ideas on how we can improve our products.

How to you add a checkbox at the bottom of a grid question?

Is it possible to add a checkbox at the very bottom of a grid question (I need to add an 'I Don't Know' checkbox).

To provide more context, I have a numeric grid (one column and 10 rows) where each of the rows is a category, and the column asks for how much you spent on each category. At the end of the 10 categories is the total on row 11 (generated by Sawtooth).

At the very bottom of the question, I would like a checkbox for respondents to check if they are unsure, and that value will be used for skip logic later on.

I am unsure on how to add it, as Sawtooth doesn't allow you to add any options of a different type, and even if that wasn't the case, this option would be placed before the "Total row"

Any advice would be much appreciated. For reference, I am using Sawtooth Version 8.3.10
asked Jul 18 by Henry Yu

1 Answer

0 votes
Start by setting your grid question to not require a response.

Next you'll want to add a checkbox-type select question that comes immediately after the grid question.  The select question needs exactly one list item.

Put this code in the select question's footer:

<style>
#[% QuestionName() %]_div {
    display: none;
}
</style>

<script>
GLOBAL_SAWTOOTH_[% QuestionName() %] = {
    gridOrientation: 'c',
    constantSumOrRanking: []
};

$(window).load(function(){
    // Question, list
    var question = $('#[% QuestionName() %]_div').prevAll('.question')[0].id.replace(/_div/, '');
    var qdiv = $('#' + question + '_div');
    var list = $('input[name=hid_list_[% QuestionName() %]]').val().split(',').map(Number);

    // Move N/A fields
    var notApplicableLabel = 'N/A';

    $('#[% QuestionName() %]_div .response_row').css('text-align', 'center');
    $('#[% QuestionName() %]_div .input_cell').css('display', 'inline-block');
    $('#[% QuestionName() %]_div .option_cell').css('display', 'inline-block');
    if ([% ScreenWidth() %] > 800) {
        $('#[% QuestionName() %]_div .mobile_select').removeClass('mobile_select');
    }

    if ($(qdiv).hasClass('semanticdiff')) {
        $('#[% QuestionName() %]_div .option_cell').remove();

        $('#' + question + '_div .column_header_row').append('<td>' + notApplicableLabel + '</td>');
        list.forEach(function(item){
            $('#' + question + '_' + item + '_row').append('<td></td>');
            $('#' + question + '_' + item + '_row > td:last')
                .append($('#[% QuestionName() %]_' + item).closest('.response_row'));
        });
    }

    else if ($(qdiv).hasClass('grid') && !$(qdiv).find('.mobile_grid').length) {
        $('#[% QuestionName() %]_div .option_cell').remove();

        if (GLOBAL_SAWTOOTH_[% QuestionName() %].gridOrientation == 'r') {
            $('#' + question + '_div .column_header_row').append('<td>' + notApplicableLabel + '</td>');
            $('#' + question + '_div .inner_table > tbody > tr:first-child > td:last-child').css('text-align', 'center');
            list.forEach(function(item){
                $('#' + question + '_r' + item + '_row').append('<td></td>');
                $('#' + question + '_r' + item + '_row > td:last-child')
                    .append($('#[% QuestionName() %]_' + item).closest('.response_row'));
            });
        }

        else {
            $('#' + question + '_div .inner_table > tbody').append('<tr><td>' + notApplicableLabel +'</td></tr>');
            $('#' + question + '_div .inner_table > tbody > tr:last-child > td').css('text-align', 'right');
            list.forEach(function(item){
                $('#' + question + '_div .inner_table > tbody > tr:last-child').append('<td></td');
                $('#' + question + '_div .inner_table > tbody > tr:last-child > td:last-child')
                    .append($('#[% QuestionName() %]_' + item).closest('.response_row'));
            });
        }

        continueGridAltColors(question);
    }

    else if ($(qdiv).hasClass('grid')) {
        $('#[% QuestionName() %]_div .option_cell label').text(notApplicableLabel);
        list.forEach(function(item){
            $('#' + question + '_div .mobile_grid_card[data-card_num=' + item + ']')
                .append($('#[% QuestionName() %]_' + item).closest('.response_row'));
        });
    }

    // N/A click event
    $('#' + question + '_div .clickable').click(function(event){
        var name = $(this).find('input[type=checkbox]:visible').attr('name');
        var bln = SSI_GetValue(name);
        if (name !== undefined) {
            updateNotApplicableVariable(question, '[% QuestionName() %]', name, bln);
        }
    });
    $(document).on('customGraphicalCheckbox', function(event, graphicalObj, inputObj, bln){
        updateNotApplicableVariable(question, '[% QuestionName() %]', inputObj.name, bln);
    });
    list.forEach(function(item){
        updateNotApplicableVariable(question, '[% QuestionName() %]', '[% QuestionName() %]_' + item, SSI_GetValue('[% QuestionName() %]_' + item));
    });
})

function updateNotApplicableVariable(baseQuestion, naQuestion, name, bln) {
    var qdiv = $('#' + baseQuestion + '_div');
    if (name.startsWith(naQuestion + '_')) {
        var match = name.match(new RegExp(naQuestion + '_([0-9]+)'));
        var item = Number(match[1]);
        
        // Col-oriented grid
        if ($(qdiv).hasClass('grid')) {
            var rows = $('input[name=hid_row_list_' + baseQuestion + ']').val().split(',').map(Number);
            
            // Disable and clear
            if (bln) {
                $('#' + baseQuestion + '_r_total_c1').val(0);
                rows.forEach(function(row){
                    if ($('#' + baseQuestion + '_c' + item + '_' + row + '[type=radio]').length) {
                        $('input[type=radio][name=' + baseQuestion + '_c' + item + ']:visible').prop('checked', false).prop('disabled', true);
                        SSI_RadioReset(baseQuestion + '_c' + item);
                        SSI_DisableGraphicalInput(baseQuestion + '_c' + item + '_' + row);
                    }
                    else if ($('#' + baseQuestion + '_r' + row + '_c' + item + '[type=checkbox]').length) {
                        $('input[type=checkbox][name^=' + baseQuestion + '_r][name$=_c' + item + ']:visible').prop('checked', false).prop('disabled', true);
                        SSI_SetSelect(baseQuestion + '_r' + row + '_c' + item, false);
                        SSI_DisableGraphicalInput(baseQuestion + '_r' + row + '_c' + item);
                    }
                    else {
                        $('#' + baseQuestion + '_r' + row + '_c' + item).val('').prop('disabled', true);
                    }
                });
            }

            // Enable
            else {
                rows.forEach(function(row){
                    if ($('#' + baseQuestion + '_c' + item + '_' + row + '[type=radio]').length) {
                        $('input[type=radio][name=' + baseQuestion + '_c' + item + ']:visible').prop('disabled', false);
                        SSI_EnableGraphicalInput(baseQuestion + '_c' + item + '_' + row);
                    }
                    else if ($('#' + baseQuestion + '_r' + row + '_c' + item + '[type=checkbox]').length) {
                        $('input[type=checkbox][name^=' + baseQuestion + '_r][name$=_c' + item + ']:visible').prop('disabled', false);
                        SSI_EnableGraphicalInput(baseQuestion + '_r' + row + '_c' + item);
                    }
                    else {
                        $('#' + baseQuestion + '_r' + row + '_c' + item).prop('disabled', false);
                    }
                });
            }
        }
    }
}

function SSI_CustomGraphicalCheckbox(graphicalObj, inputObj, bln) {
    $(document).trigger('customGraphicalCheckbox', [graphicalObj, inputObj, bln]);
}

function continueGridAltColors(question) {
    var tbody = $('#' + question + '_div .inner_table > tbody');
    var firstTdAltColor1 = $(tbody).find('> tr:first-child > td:nth-child(1)').hasClass('alt_color1');
    var secondTdAltColor1 = $(tbody).find('> tr:first-child > td:nth-child(2)').hasClass('alt_color1');
    $(tbody).find('> tr > td').removeClass('alt_color1 alt_color2');

    var trs = $(tbody).find('> tr').length;
    var tds = $(tbody).find('> tr:first-child > td').length;

    // Alternates by row
    if ((firstTdAltColor1 && secondTdAltColor1) || (!firstTdAltColor1 && !secondTdAltColor1)) {
        var altColor = firstTdAltColor1 ? 'alt_color1' : 'alt_color2';
        for (var r = 1; r <= trs; r++) {
            for (var c = 1; c <= tds; c++) {
                $(tbody).find('> tr:nth-child(' + r + ') > td:nth-child(' + c + ')').addClass(altColor);
            }
            altColor = (altColor == 'alt_color1') ? 'alt_color2' : 'alt_color1';
        }
    }

    // Alternates by column
    else {
        var altColor = firstTdAltColor1 ? 'alt_color1' : 'alt_color2';
        for (var c = 1; c <= tds; c++) {
            for (var r = 1; r <= trs; r++) {
                $(tbody).find('> tr:nth-child(' + r + ') > td:nth-child(' + c + ')').addClass(altColor);
            }
            altColor = (altColor == 'alt_color1') ? 'alt_color2' : 'alt_color1';
        }
    }
}
</script>


(cont.)
answered Jul 18 by Zachary Platinum Sawtooth Software, Inc. (63,900 points)
Then give the select question this custom JavaScript verification to be ran before system verification:

var question = $('#[% QuestionName() %]_div').prevAll('.question')[0].id.replace(/_div/, '');
var qdiv = $('#' + question + '_div');
var list = $('input[name=hid_list_[% QuestionName() %]]').val().split(',').map(Number);
var err;

for (var i = 0; i < list.length && !err; i++) {
    var item = list[i];
    if (!SSI_GetValue('[% QuestionName() %]_' + item)) {
        // Sem diff
        if ($(qdiv).hasClass('semanticdiff')) {
            err = !SSI_GetValue(question + '_' + item);
        }

        // Row-oriented grid
        else if ($(qdiv).hasClass('grid') && GLOBAL_SAWTOOTH_[% QuestionName() %].gridOrientation == 'r') {
            var cols = $('input[name=hid_col_list_' + question + ']').val().split(',').map(Number);
            
            // Radio
            if ($('#' + question + '_r' + item + '_' + cols[0] + '[type=radio]').length) {
                err = !SSI_GetValue(question + '_r' + item);
            }

            // Check
            else if ($('#' + question + '_r' + item + '_c' + cols[0] + '[type=checkbox]').length) {
                
            }

            // Combo
            else if ($('select#' + question + '_r' + item + '_c' + cols[0]).length && GLOBAL_SAWTOOTH_[% QuestionName() %].constantSumOrRanking.indexOf(item) == -1) {
                for (var c = 0; c < cols.length && !err; c++) {
                    err = !SSI_GetValue(question + '_r' + item + '_c' + cols[c]);
                }
            }

            // Open end, numeric
            else if (GLOBAL_SAWTOOTH_[% QuestionName() %].constantSumOrRanking.indexOf(item) == -1) {
                for (var c = 0; c < cols.length && !err; c++) {
                    err = !$('#' + question + '_r' + item + '_c' + cols[c]).val().length;
                }
            }

            // Combo ranking
            else if ($('select#' + question + '_r' + item + '_c' + cols[0]).length) {
                err = true;
                for (var c = 0; c < cols.length && err; c++) {
                    err = !SSI_GetValue(question + '_r' + item + '_c' + cols[c]);
                }
            }

            // Constant sum, tel ranking
            else {
                err = true;
                for (var c = 0; c < cols.length && err; c++) {
                    err = !$('#' + question + '_r' + item + '_c' + cols[c]).val().length;
                }
            }
        }

        // Col-oriented grid
        else {
            var rows = $('input[name=hid_row_list_' + question + ']').val().split(',').map(Number);
            
            // Radio
            if ($('#' + question + '_c' + item + '_' + rows[0] + '[type=radio]').length) {
                err = !SSI_GetValue(question + '_c' + item);
            }

            // Check
            else if ($('#' + question + '_r' + rows[0] + '_c' + item + '[type=checkbox]').length) {
                
            }

            // Combo
            else if ($('select#' + question + '_r' + rows[0] + '_c' + item).length && GLOBAL_SAWTOOTH_[% QuestionName() %].constantSumOrRanking.indexOf(item) == -1) {
                for (var r = 0; r < rows.length && !err; r++) {
                    err = !SSI_GetValue(question + '_r' + rows[r] + '_c' + item);
                }
            }

            // Open end, numeric
            else if (GLOBAL_SAWTOOTH_[% QuestionName() %].constantSumOrRanking.indexOf(item) == -1) {
                for (var r = 0; r < rows.length && !err; r++) {
                    err = !$('#' + question + '_r' + rows[r] + '_c' + item).val().length;
                }
            }

            // Combo ranking
            else if ($('select#' + question + '_r' + rows[0] + '_c' + item).length) {
                err = true;
                for (var r = 0; r < rows.length && err; r++) {
                    err = !SSI_GetValue(question + '_r' + rows[r] + '_c' + item);
                }
            }

            // Constant sum, tel ranking
            else {
                err = true;
                for (var r = 0; r < rows.length && err; r++) {
                    err = !$('#' + question + '_r' + rows[r] + '_c' + item).val().length;
                }
            }
        }
    }
}

// Error
$(qdiv).removeClass('error_quest_highlight2');
$('#' + question + '_err2').remove();

if (err) {
    strErrorMessage = 'Question must be answered or "Not Applicable" must be selected.';

    $(qdiv).removeClass('error_quest_highlight');
    $('#' + question + '_err').remove();
    $(qdiv).addClass('error_quest_highlight2');
    $(qdiv).prepend('<div id="' + question + '_err2" class="question_error_box error_messages"></div>');
    $('#' + question + '_err2').append('<div class="question_errors">' + strErrorMessage + '</div>');
}
Zachary,

I'm using your RangedDndRanking code in a survey and the client wants to add a checkbox at the bottom if the respondent doesn't want to rank any of the items.  Can the code above be modified for this?
Your best bet would probably be to put "Not Applicable: Per Question" after the first helper from "Ranged Drag-and-Drop Ranking."  The not applicable code would probably need to be changed so that it points to the ranking question rather than to the helper.
...