Dependent HTML5 sliders

A various amount of HTML input range fields that are connected to each other, and will automatically correct the others when one is changed.

by Annie Lagang

HTML

<h4>Dependent sliders</h4>
<br>

<input type="range" min="0" max="100" step="1" value="0">
<input type="range" min="0" max="100" step="1" value="0">
<input type="range" min="0" max="100" step="1" value="0">
<input type="range" min="0" max="100" step="1" value="0">

CSS

* { margin: 0; padding: 0; }
body { padding: 10px; }

input[type="range"] {
    position: relative;
    display: block;
    width: 300px;
}
input[type="range"]::after {
    content: attr(value);
    position: absolute;
    left: 305px;
}

JavaScript

var elements = document.querySelectorAll('input');
var length = elements.length;
var sliders = Array.prototype.slice.call(elements); // Copy of `elements` but as a real array
var max = 100;

function change(current) {
    "use strict";
    
    set(current);

    var input = +current.value;
    var delta = max - input;
    var sum = 0;
    var siblings = [];

    // Sum of all siblings
    sliders.forEach(function (slider) {
        if (current != slider) {
            siblings.push(slider); // Register as sibling
            sum += +slider.value;
        }
    });

    // Update all the siblings
    var partial = 0;
    siblings.forEach(function (slider, i) {
        var val = +slider.value;
        var fraction = 0;

        // Calculate fraction
        if (sum <= 0) {
            fraction = 1 / (length - 1)
        } else {
            fraction = val / sum;
        }

        // The last element will correct rounding errors
        if (i >= length - 1) {
            val = max - partial;
        } else {
            val = Math.round(delta * fraction);
            partial += val;
        }

        set(slider, val);
    });
}

// Set value on a slider
function set(elm, val) {
    if (val) {
        elm.value = val;
    }
    // Hack to trigger CSS ::after content to be updated
    elm.setAttribute('value', elm.value);
}

// Add event listeners to the DOM elements
for (var i = 0, l = elements.length; i < l; i++)  {
    elements[i].addEventListener('change', function (e) {
        change(this);
    }, false);
}