Search code examples
javascriptjquerybootstrap-4slider

Combined total for multiple Sliders (Bootstrap v4 & jQuery)


I'm trying to implement a page with 3 sliders. The combined total of all 3 sliders should never go over 100%.

The page uses the Bootstrap 4 framework and jQuery 3.6.2

These is where I struggle: The combined value of all three sliders should never be over 100%. Increasing one slider should decrease the other two values and vice versa.

Here is what I have done so far:

HTML

<div id="sliders">
    <div class="row">   
        <label for="Focus1" class="form-label">Slider 1</label>
    </div>
    <div class="row">
        <input id="sl1" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="34" data-value="34" value="34" style="display: none;">
        <span id="sl1CurrentSliderValLabel">&nbsp;&nbsp;<span class="value" id="sl1SliderVal">34</span>%</span>
    </div>
    <div class="row">
        <label for="Focus2" class="form-label">Slider 2</label>
    </div>
    <div class="row">
        <input id="sl2" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="33" data-value="33" value="33" style="display: none;"/>
        <span id="sl2CurrentSliderValLabel">&nbsp;&nbsp;<span class="value" id="sl2SliderVal">33</span>%</span>
    </div>
    <div class="row">
        <label for="Focus3" class="form-label">Slider 3</label>
    </div>
    <div class="row">
        <input  id="sl3" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="33" data-value="33" value="33" style="display: none;"/>
        <span id="sl3CurrentSliderValLabel">&nbsp;&nbsp;<span class="value" id="sl3SliderVal">33</span>%</span>
    </div>
</div>

JS `

<script>
    $(document).ready(function() {
        $('.tooltip').tooltip();

        /* Slider 1 */
        $("#sl1").slider();
        $("#sl1").on('slide', function(slideEvt) {
           $("#sl1SliderVal").text(slideEvt.value);
        });
        
        /* Slider 2 */
        $("#sl2").slider();
        $("#sl2").on('slide', function(slideEvt) {
           $("#sl2SliderVal").text(slideEvt.value);
        });
        
        /* Slider 3 */
        $("#sl3").slider();
        $("#sl3").on('slide', function(slideEvt) {
           $("#sl3SliderVal").text(slideEvt.value);
        });
      
    });
        
    /* Start Slider Logic */
    var sliders = $("#sliders .slider");
    var availableTotal = 100;

    sliders.each(function() {
        var init_value = parseInt($(this).text());

        $(this).siblings('.value').text(init_value);

        $(this).empty().slider({
            value: init_value,
            min: 0,
            max: availableTotal,
            range: "max",
            step: 2,
            animate: 0,
            slide: function(event, ui) {
                
                // Update display to current value
                $(this).siblings('.value').text(ui.value);

                // Get current total
                var total = 0;

                sliders.not(this).each(function() {
                    total += $(this).slider("option", "value");
                });

                total += ui.value;

                var delta = availableTotal - total;
                
                // Update each slider
                sliders.not(this).each(function() {
                    var t = $(this),
                        value = t.slider("option", "value");

                    var new_value = value + (delta/2);
                    
                    if (new_value < 0 || ui.value == 100) 
                        new_value = 0;
                    if (new_value > 100) 
                        new_value = 100;

                    t.siblings('.value').text(new_value);
                    t.slider('value', new_value);
                });
            }
        });
    });
    
</script>`

CSS

<link href="../css/bootstrap.min.css" rel="stylesheet">
<link href="../css/bootstrap-slider.css" rel="stylesheet">

Thanks a lot for your help!


Solution

  • The main problem was to use the correct API call at the right time.

    This is the correct code to make the sliders work as intended:

    JS `

    <script>
        $(document).ready(function() {
            $('.tooltip').tooltip();
            
            var sliders = $("#sliders .slider");
            var availableTotal = 100;
    
            sliders.each(function() {
            var init_value = parseInt(this.value);
    
            $(this).siblings('.value').text(init_value);
    
            $(this).empty().slider({
                value: init_value,
                min: 0,
                max: availableTotal,
                range: false,
                step: 2,
                animate: 0
            });
            
            $(this).on( "slide", function( event ) {
                        var slVal = parseInt($(this).val());
                
                        // Update display to current value
                        $(this).siblings('.value').text(slVal);
    
                        // Get current total
                        var total = 0;
    
                        sliders.not(this).each(function() {
                            total += parseInt($(this).val());
                        });
    
                        // Need to do this because apparently jQ UI
                        // does not update value until this event completes
                        total += slVal;
    
                        var delta = availableTotal - total;
                        
                        // Update each slider
                        sliders.not(this).each(function() {
                            var t = $(this),
                                value = parseInt(t.slider('getValue'));
    
                            var new_value = parseInt(value + (delta/2));
                            
                            if (new_value < 0 || slVal == 100) 
                                new_value = 0;
                            if (new_value > 100) 
                                new_value = 100;
    
                            t.siblings('.value').text(new_value);
                            t.slider('setValue',new_value);
                        });} );
                });
        
    
        });
        
    
    </script>`
    

    HTML

    <div id="sliders">
            <div class="row g-6">   
            <label for="Focus1" class="form-label">Slider 1</label>
            </div>
            <div class="row g-6">
            <input class="slider" id="sl1" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="34" data-value="34" value="34" style="display: none;">
            &nbsp;&nbsp;<span class="value" id="sl1SliderVal">34</span><span id="sl1CurrentSliderValLabel">%</span>
            </div>
            <div class="row g-6">
            <label for="Focus2" class="form-label">Slider 2</label>
            </div>
            <div class="row g-6">
            <input class="slider" id="sl2" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="33" data-value="33" value="33" style="display: none;"/>
            &nbsp;&nbsp;<span class="value" id="sl2SliderVal">33</span><span id="sl2CurrentSliderValLabel">%</span>
            </div>
            <div class="row g-6">
            <label for="Focus3" class="form-label">Slider 3</label>
            </div>
            <div class="row g-6">
            <input class="slider" id="sl3" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="33" data-value="33" value="33" style="display: none;"/>
            &nbsp;&nbsp;<span class="value" id="sl3SliderVal">33</span><span id="sl3CurrentSliderValLabel">%</span>
            </div>
        </div>
    

    The CSS section from the question is unchanged.