URI: 
       add multirange input range scripts - jscancer - Javascript crap (relatively small)
  HTML git clone git://git.codemadness.org/jscancer
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 7f471d96bbdab4f5ce0605076805f378f730ebe7
   DIR parent 2eb5ad4110cabfa6ebd65a0e631a7c1c49901d48
  HTML Author: Hiltjo Posthuma <hiltjo@codemadness.org>
       Date:   Thu, 23 Jul 2026 19:26:52 +0200
       
       add multirange input range scripts
       
       Diffstat:
         A multirange/README                   |      42 +++++++++++++++++++++++++++++++
         A multirange/example.html             |     130 +++++++++++++++++++++++++++++++
         A multirange/filter.js                |     100 +++++++++++++++++++++++++++++++
       
       3 files changed, 272 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/multirange/README b/multirange/README
       @@ -0,0 +1,41 @@
       +multi range filter
       +==================
       +
       +Multiple input range filter.
       +Intended for use-case where you need to select a minimum and maximum value
       +within a certain range.
       +
       +Where each input range restricts min, max to eachother.
       +
       +Requires a modern browser that supports the list attribute (for ticks and
       +snapping to a nearest value).
       +
       +
       +FEATURES
       +--------
       +
       +- Small:
       +  - Filesize: +- 3KB (+- 1KB GZIP).
       +  - Lines: +- 99, not much code, so hopefully easy to understand.
       +  - No dependencies on other libraries like jQuery.
       +- (Graceful) fallback to input range.
       +- Support manual keyboard input for ranges.
       +- Ticks and snapping to a value (requires browser support).
       +- Custom label function to show depending on the value.
       +- Detect decimal values, formatting them nicely (maximum digits required).
       +- Permissive ISC license, see LICENSE file.
       +- Supported browsers are:
       +  - Firefox and Firefox ESR.
       +  - Chrome and most recent webkit-based browsers.
       +
       +
       +EXAMPLES
       +--------
       +
       +See example.html for an example.
       +
       +
       +Author
       +------
       +
       +Hiltjo Posthuma <hiltjo@codemadness.org>
       +\ No newline at end of file
   DIR diff --git a/multirange/example.html b/multirange/example.html
       @@ -0,0 +1,129 @@
       +<html>
       +<head>
       +<title>Example</title>
       +</head>
       +<body>
       +
       +<style type="text/css">
       +label {
       +        font-weight: bold;
       +}
       +input:invalid {
       +        border-color: #ff0000 !important;
       +        box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(255, 0, 0, 0.3) !important;
       +}
       +</style>
       +
       +<form autocomplete="off">
       +
       +<h2>Example: basic example</h2>
       +
       +<table class="filterslider" data-from="filter_number_from[0]" data-to="filter_number_to[0]" data-label-from="filter_number_from_value[0]" data-label-to="filter_number_to_value[0]">
       +        <tr>
       +                <td class="label"><label for="filter_number_from[0]">From:</label></td>
       +                <td class="input"><input class="slider" type="range" step="1" value="0" name="filter_number_from[0]" id="filter_number_from[0]" min="0" max="1000" /></td>
       +                <td class="value"><span id="filter_number_from_value[0]">0</span></td>
       +                <td class="measure">meter</td>
       +        </tr>
       +        <tr>
       +                <td class="label"><label for="filter_number_to[0]">Through:</label></td>
       +                <td class="input"><input class="slider" type="range" step="1" value="1000" name="filter_number_to[0]" id="filter_number_to[0]" min="0" max="1000" /></td>
       +                <td class="value" id="filter_number_to_value[0]">1000</td>
       +                <td class="measure">meter</td>
       +        </tr>
       +</table>
       +
       +<hr/>
       +
       +<h2>Example: basic example with ticks</h2>
       +
       +<p>
       +<i>A datalist is used for ticks and snapping to values.</i>
       +</p>
       +
       +<table class="filterslider" data-from="filter_number_from[1]" data-to="filter_number_to[1]" data-label-from="filter_number_from_value[1]" data-label-to="filter_number_to_value[1]">
       +        <tr>
       +                <td class="label"><label for="filter_number_from[1]">From:</label></td>
       +                <td class="input"><input class="slider" type="range" step="1" value="0" name="filter_number_from[1]" id="filter_number_from[1]" min="0" max="1000" list="filter_values_1" /></td>
       +                <td class="value"><span id="filter_number_from_value[1]">0</span></td>
       +                <td class="measure">meter</td>
       +        </tr>
       +        <tr>
       +                <td class="label"><label for="filter_number_to[1]">Through:</label></td>
       +                <td class="input"><input class="slider" type="range" step="1" value="1000" name="filter_number_to[1]" id="filter_number_to[1]" min="0" max="1000" list="filter_values_1" /></td>
       +                <td class="value" id="filter_number_to_value[1]">1000</td>
       +                <td class="measure">meter</td>
       +        </tr>
       +</table>
       +<datalist id="filter_values_1">
       +        <option value="0" label="0"></option>
       +        <option value="250" label="250"></option>
       +        <option value="500" label="500"></option>
       +        <option value="650" label="650"></option>
       +        <option value="1000" label="1000"></option>
       +</datalist>
       +
       +<hr/>
       +
       +<h2>Example: with decimals</h2>
       +
       +<p>
       +<i>The step attribute must be set to "any" for some browsers for decimal values.</i>
       +</p>
       +
       +<table class="filterslider" data-decimals="datalist" data-from="filter_number_from[2]" data-to="filter_number_to[2]" data-label-from="filter_number_from_value[2]" data-label-to="filter_number_to_value[2]">
       +        <tr>
       +                <td class="label"><label for="filter_number_from[2]">From:</label></td>
       +                <td class="input"><input class="slider" type="range" step="any" value="0" name="filter_number_from[2]" id="filter_number_from[2]" min="0" max="1" list="filter_values_2" /></td>
       +                <td class="value"><span id="filter_number_from_value[2]">0</span></td>
       +                <td class="measure">meter</td>
       +        </tr>
       +        <tr>
       +                <td class="label"><label for="filter_number_to[2]">Through:</label></td>
       +                <td class="input"><input class="slider" type="range" step="any" value="1" name="filter_number_to[2]" id="filter_number_to[2]" min="0" max="1" list="filter_values_2" /></td>
       +                <td class="value" id="filter_number_to_value[2]">1</td>
       +                <td class="measure">meter</td>
       +        </tr>
       +</table>
       +<datalist id="filter_values_2">
       +        <option value="0.0" label="0"></option>
       +        <option value="0.25" label="0.25"></option>
       +        <option value="0.50" label="0.50"></option>
       +        <option value="0.65" label="0.65"></option>
       +        <option value="0.90" label="0.9"></option>
       +        <option value="1.00" label="1.0"></option>
       +</datalist>
       +
       +<hr/>
       +
       +<h2>Example: example allowing also manual input</h2>
       +
       +<table class="filterslider" data-from="filter_number_from[3]" data-to="filter_number_to[3]" data-label-from="filter_number_from_value[3]" data-label-to="filter_number_to_value[3]">
       +        <tr>
       +                <td class="label"><label for="filter_number_from[3]">From:</label></td>
       +                <td class="input"><input class="slider" type="range" step="1" value="0" name="filter_number_from[3]" id="filter_number_from[3]" min="0" max="1000" list="filter_values_3" /></td>
       +                <td class="value"><input id="filter_number_from_value[3]" type="number" value="0" step="1" min="0" max="1000" /></td>
       +                <td class="measure">meter</td>
       +        </tr>
       +        <tr>
       +                <td class="label"><label for="filter_number_to[3]">Through:</label></td>
       +                <td class="input"><input class="slider" type="range" step="1" value="1000" name="filter_number_to[3]" id="filter_number_to[3]" min="0" max="1000" list="filter_values_3" /></td>
       +                <td class="value"><input id="filter_number_to_value[3]" type="number" value="1000" step="1" min="0" max="1000" /></td>
       +                <td class="measure">meter</td>
       +        </tr>
       +</table>
       +<datalist id="filter_values_3">
       +        <option value="0" label="0"></option>
       +        <option value="250" label="250"></option>
       +        <option value="500" label="500"></option>
       +        <option value="650" label="650"></option>
       +        <option value="1000" label="1000"></option>
       +</datalist>
       +
       +</form>
       +
       +<script type="text/javascript" src="filter.js"></script>
       +
       +</body>
       +
       +</html>
       +\ No newline at end of file
   DIR diff --git a/multirange/filter.js b/multirange/filter.js
       @@ -0,0 +1,99 @@
       +var sliders = document.getElementsByClassName("filterslider");
       +for (var i = 0; i < sliders.length; i++) {
       +        var slider = sliders[i];
       +        var fromel = document.getElementById(slider.getAttribute("data-from") || "");
       +        var toel = document.getElementById(slider.getAttribute("data-to") || "");
       +        if (fromel === null || toel === null)
       +                continue;
       +        
       +        // datalist: ticks and for snapping to value.
       +        var datalistel = null;
       +        var datalist = fromel.getAttribute("list") || "";
       +        if (datalist !== "") {
       +                datalistel = document.getElementById(datalist);
       +                if (datalistel === null)
       +                        continue;
       +        }
       +
       +        // decimals format based on datalist values.
       +        var decimals = slider.getAttribute("data-decimals") || "0";
       +        if (decimals === "datalist" && datalistel !== null) {
       +                var maxdecimals = 0;
       +                var countdecimals = function(v) {
       +                        if (Math.floor(v) !== v) {
       +                                var vs = v.toString().split(".");
       +                                return vs.length > 1 ? (vs[1].length || 0) : 0;
       +                        }
       +                        return 0;
       +                };
       +                var options = datalistel.options || [];
       +                for (var j = 0; j < options.length; j++) {
       +                        var d = countdecimals(options[j].value);
       +                        if (d > maxdecimals)
       +                                maxdecimals = d;
       +                }
       +                decimals = maxdecimals;
       +        } else {
       +                decimals = parseInt(decimals) || 0;
       +        }
       +        
       +        var cfg = {
       +                from: fromel,
       +                to: toel,
       +                labelfrom: document.getElementById(slider.getAttribute("data-label-from") || ""),
       +                labelto: document.getElementById(slider.getAttribute("data-label-to") || ""),
       +                decimals: decimals < 0 ? 0 : decimals
       +        };
       +        
       +        cfg.labelfn = (function(dec) {
       +                        return function(v) {
       +                                return Number(v).toFixed(dec);
       +                        };
       +        })(cfg.decimals);
       +
       +        // from
       +        cfg.inputrangefrom = (function(c) {
       +                return function(e) {
       +                        if (parseFloat(c.from.value) > parseFloat(c.to.value))
       +                                c.from.value = c.to.value;
       +                        if (c.labelfrom !== null)
       +                                c.labelfrom[c.labelfrom.tagName === "INPUT" ? "value" : "innerText"] = c.labelfn(c.from.value);
       +                };
       +        })(cfg);
       +        fromel.addEventListener("input", cfg.inputrangefrom);
       +
       +        // to
       +        cfg.inputrangeto = (function(c) {
       +                return function(e) {
       +                        if (parseFloat(c.to.value) < parseFloat(c.from.value))
       +                                c.to.value = c.from.value;
       +                        if (c.labelto !== null)
       +                                c.labelto[c.labelto.tagName === "INPUT" ? "value" : "innerText"] = c.labelfn(c.to.value);
       +                };
       +        })(cfg);
       +        toel.addEventListener("input", cfg.inputrangeto);
       +        
       +        // allow manual input if label is input element.
       +        if (cfg.labelfrom != null && cfg.labelfrom.tagName === "INPUT") {
       +                cfg.inputnumberfrom = (function(c) {
       +                        return function() {
       +                                this.max = c.to.value;
       +                                c.from.value = this.value;
       +                        };
       +                })(cfg);
       +                cfg.labelfrom.addEventListener("input", cfg.inputnumberfrom);
       +        }
       +        if (cfg.labelto != null && cfg.labelto.tagName === "INPUT") {
       +                cfg.inputnumberto = (function(c) {
       +                        return function() {
       +                                this.min = c.from.value;
       +                                c.to.value = this.value;
       +                        };
       +                })(cfg);
       +                cfg.labelto.addEventListener("input", cfg.inputnumberto);
       +        }
       +        
       +        // call on init.
       +        cfg.inputrangefrom();
       +        cfg.inputrangeto();
       +}
       +\ No newline at end of file