Have an idea?

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

Reset button for Constant Sum Slider

Hi,

In the library we discovered a ready-to-use code for a constant sum slider (LINK: https://www.sawtoothsoftware.com/community-question-library/1755-slider-constant-sum).

We aimed at including a reset button in order to set the values of each slider back to 0. The button works. However, when moving the sliders again the lastly saved values appear. It is therefore not reset completely to 0. We assume it has something to do with recalling the short-term and long-term values but could not figure out how to recall them properly.

Please find the code below:

<p> <input id="reset" type="button" value="Reset" class="reset-button" onclick="Reset()"></p>

<script>
document.getElementById('reset').onclick = function(){
       $('.slider[data-id="[% QuestionName() %]_' + 1 + '"]').slider('value',0);
    $('.slider[data-id="[% QuestionName() %]_' + 2 + '"]').slider('value',0);
    $('.slider[data-id="[% QuestionName() %]_' + 3 + '"]').slider('value',0);
    $('#[% QuestionName() %]_' + 1).val(0);
    $('#[% QuestionName() %]_' + 2).val(0);
    $('#[% QuestionName() %]_' + 3).val(0);
   $j("#SubmitDistribution").attr("disabled", true);
   updateConstantSumTotalIfExists();
   var values = [0,0,0];
   sliderStartEvent(values);
   sliderSlideEvent(values);

   //$('#[% QuestionName() %]_' + key).val(tempValue);
   //$('.slider[data-id="[% QuestionName() %]_' + key + '"]').slider('value', tempValue);
   //$('.slider').value = 0;
};

</script>



Could you support here?

Thank you very much!
Laura
asked Apr 23 by Laura

1 Answer

0 votes
You've got a pretty good start.  You are correct in noticing that there are short and long term values in the code.  In order to be able to modify the long term values, we have to either (a) modify the existing code to make these values available outside of the document.ready, or (b) move your new code so that it exists in the document.ready.  I think I would prefer to do the latter.

The other tricky part of the code that I would point out is "reachedSum."  This is the variable that changes the behavior of the code once the responses have reached the question's total.

So I would remove the "onclick" part of the HTML, and then add this near the end of the document.ready code:

    // Reset
    $('#reset').click(function(){
        list.forEach(function(item){
            $('#[% QuestionName() %]_' + item).val(0);
            $('.slider[data-id="[% QuestionName() %]_' + item + '"]').slider('value', 0);
            values[item] = 0;
        });
        updateConstantSumTotalIfExists();
        reachedSum = false;
    });
answered Apr 23 by Zachary Platinum Sawtooth Software, Inc. (148,450 points)
I'm not sure what "SubmitDistribution" is referring to in the code excerpt you've posted.  You could add it to my code above if you need it, but I would note that you may have a typo using "$j" instead of "$" or "jQuery".
Hi Zach, thanks a lot. That helped very much already!

The 'submit distribution' is basically a button which should only be activated once the sum of 100 is reached (and only when clicking on it, the 'next' button appears to allow the user to move forward). By clicking on it, the currently selected slider values are saved and displayed, but the user can still make changes (either pull the sliders directly or press reset). In case of reset however, the 'save answer' button should be deactived again UNTIL 100 is reached.

I added this line again to the reset part. It seems that it deactivates the submit button but does not re-activate it once 100 is reached. Do you have an idea what could be the issue? I attach the whole code for you as reference below.

Further, we would like to know whether there is a possibility to customize the slider buttons, e.g. using:

.slider-button {
  cursor: pointer;
  background: #4CAF50;
  color: white;
}


<input type="button" id="SubmitDistribution" class="submit-button" value="Save answer" disabled=true onclick="enableNext()"</input></div>

<script>
$(document).ready(function(){
    // Parameters
    var constantSumTotal = 100;
    
    // Switch input and list labels
    $('#[% QuestionName() %]_div .option_cell').each(function(){
        $(this).parent().prepend($(this));
    });

    // Add sliders
    var list = $('input[name="hid_list_[% QuestionName() %]"]').val().split(',');
    list.forEach(function(item){
        $('#[% QuestionName() %]_' + item).closest('tr').append('<td><div class="slider" data-id="[% QuestionName() %]_' + item + '"></div></td>');
    });
    $('#[% QuestionName() %]_total').closest('tr').append('<td></td>');

    // Create values object for long-term data (updated after focusout / slider.stop)
    var values = {};
    list.forEach(function(item){
        values[item] = SSI_GetValue('[% QuestionName() %]_' + item);
    });

    // Create values2 object for short-term data (updated after keyup / slider.slide)
    var values2 = {};
    function cloneValuesObject() {
        values2 = {};
        list.forEach(function(item){
            values2[item] = values[item];
        });
    }
    cloneValuesObject();

    // Measure sum of values
    var reachedSum = false;
    var initialSum = 0;
    for (var key in values) {
        initialSum += values[key];
    }
    if (initialSum >= constantSumTotal) {
        reachedSum = true;
    }
    if (reachedSum = true) {
        $("#SubmitDistribution").attr("disabled", false);
        }

    // Constant sum input events
    var thisItem,
        otherSum;

    $('#[% QuestionName() %]_div .numeric_input').focusin(function(){
        thisItem = $(this).attr('id').match(/[% QuestionName() %]_([0-9]+)/)[1];
        sliderStartEvent();
    });

    $('#[% QuestionName() %]_div .numeric_input').keyup(function(){
        var value = valueInRange(SSI_GetValue($(this).attr('id')));
        $('.slider[data-id="[% QuestionName() %]_' + thisItem + '"]').slider('value', value);
        sliderSlideEvent(value);
    })

    $('#[% QuestionName() %]_div .numeric_input').focusout(function(){
        var value = valueInRange(SSI_GetValue($(this).attr('id')));
        sliderStopEvent();
    });

    // Slider events
    $('.slider').each(function(){
        $(this).slider({
            min: 0,
            max: constantSumTotal,
            value: SSI_GetValue($(this).data('id')),
            start: function(event, ui){
                thisItem = $(ui.handle.parentElement).data('id').match(/[% QuestionName() %]_([0-9]+)/)[1];
                sliderStartEvent();
            },
            slide: function(event, ui){
                $('#' + $(ui.handle.parentElement).data('id')).val(ui.value);
                updateConstantSumTotalIfExists();
                sliderSlideEvent(ui.value);
            },
            stop: function(event, ui){
                $('#' + $(ui.handle.parentElement).data('id')).val(ui.value);
                updateConstantSumTotalIfExists();
                sliderStopEvent();
            }
        });
    });

    function updateConstantSumTotalIfExists() {
        if ($('#[% QuestionName() %]_total').length) {
            SSI_Total('[% QuestionName() %]_*', list, 'total');
        }
    }

    // Event contents
    function sliderStartEvent() {
        otherSum = 0;
        list.forEach(function(item){
            if (item != thisItem) {
                otherSum += values[item];
            }
        });
    }

    function sliderSlideEvent(value) {
        var diff = constantSumTotal - value - otherSum;
        values[thisItem] = value;
        values2[thisItem] = value;
        if ((diff <= 0) || (diff > 0 && reachedSum)) {
            reachedSum = true;
            remainder = 0;
            cloneValuesObject();
            for (var key in values2) {
                var tempValue = values2[key];
                if (key != thisItem && tempValue != 0) {
                    tempValue = valueInRange(tempValue + roundWithRemainder(tempValue * diff / otherSum));
                    values2[key] = tempValue;
                    $('#[% QuestionName() %]_' + key).val(tempValue);
                    $('.slider[data-id="[% QuestionName() %]_' + key + '"]').slider('value', tempValue);
                }
            }
        }
    }

    function sliderStopEvent() {
        for (var key in values2) {
            if (key != thisItem) {
                values[key] = values2[key];
            }
        }
    }

    // Force value to be between 0 and constant sum total
    function valueInRange(value, constantSumTotal) {
        if (value > constantSumTotal) {
            return constantSumTotal;
        }
        if (value < 0) {
            return 0;
        }
        return value;
    }

    // Rounding
    var remainder;
    function roundWithRemainder(input) {
        var inputAndRem = input + remainder;
        var output;
        if (inputAndRem >= 0) {
            output = Math.floor(inputAndRem + 0.5);
        }
        else {
            output = Math.ceil(inputAndRem - 0.5);
        }
        remainder = inputAndRem - output;
        return output;
    }
    
        // Reset
    $('#reset').click(function(){
        list.forEach(function(item){
            $('#[% QuestionName() %]_' + item).val(0);
            $('.slider[data-id="[% QuestionName() %]_' + item + '"]').slider('value', 0);
            values[item] = 0;
        });
        updateConstantSumTotalIfExists();
        reachedSum = false;
        $("#SubmitDistribution").attr("disabled", true);
    });
})


$("#SubmitDistribution").click(function() {
    var values={};
    values[1] = $('.slider[data-id="[% QuestionName() %]_' + 1 + '"]').slider('value');
    values[2] = $('.slider[data-id="[% QuestionName() %]_' + 2 + '"]').slider('value');
    values[3] = $('.slider[data-id="[% QuestionName() %]_' + 3 + '"]').slider('value');
    var message = "Answer sent, please click 'Next' to continue. Your answer is: " + values[1] + ", " + values[2] + ", " + values[3];
    $("#YourAnswer").text(message);
});

function disableNext(){
$('.submit_div').hide()
}

window.onload=disableNext

function enableNext(){
     $('.submit_div').show()
}

</script>



Thank you very much!
This code is not right:

if (reachedSum = true) {
    $("#SubmitDistribution").attr("disabled", false);
}


"=" is the operator for setting values; "==" is the operator for testing equality.  Additionally, the code at this location only runs once, soon after the page opens.  For code like this to work, it needs to be somewhere that will run everytime a slider is moved.  "sliderSlideEvent" or "sliderStopEvent" should do the trick.  Something like this:

    function sliderStopEvent() {
        for (var key in values2) {
            if (key != thisItem) {
                values[key] = values2[key];
            }
        }

        var sum = 0;
        list.forEach(function(item){
            sum += SSI_GetValue('[% QuestionName() %]_' + item);
        });
        if (sum == constantSumTotal) {
            $('#SubmitDistribution').attr('disabled', false);
        }
    }
...