HTML5: 滑块可能有两个输入?

是否有可能使一个 HTML5滑块具有两个输入值,例如选择一个价格范围?如果是这样,怎样才能做到呢?

289404 次浏览

不,HTML5范围输入只接受一个输入。我建议你使用类似于 用户界面范围滑块的东西来完成这个任务。

一段时间以来,我一直在寻找一个轻量级的、无依赖的双滑块(仅仅为此而导入 jQuery 似乎有些疯狂) ,但似乎没有多少这样的东西。我最后修改了一下 @ Wildhoney 的密码,并且非常喜欢它。

function getVals(){
// Get slider values
var parent = this.parentNode;
var slides = parent.getElementsByTagName("input");
var slide1 = parseFloat( slides[0].value );
var slide2 = parseFloat( slides[1].value );
// Neither slider will clip the other, so make sure we determine which is larger
if( slide1 > slide2 ){ var tmp = slide2; slide2 = slide1; slide1 = tmp; }
  

var displayElement = parent.getElementsByClassName("rangeValues")[0];
displayElement.innerHTML = slide1 + " - " + slide2;
}


window.onload = function(){
// Initialize Sliders
var sliderSections = document.getElementsByClassName("range-slider");
for( var x = 0; x < sliderSections.length; x++ ){
var sliders = sliderSections[x].getElementsByTagName("input");
for( var y = 0; y < sliders.length; y++ ){
if( sliders[y].type ==="range" ){
sliders[y].oninput = getVals;
// Manually trigger event first time to display values
sliders[y].oninput();
}
}
}
}
  section.range-slider {
position: relative;
width: 200px;
height: 35px;
text-align: center;
}


section.range-slider input {
pointer-events: none;
position: absolute;
overflow: hidden;
left: 0;
top: 15px;
width: 200px;
outline: none;
height: 18px;
margin: 0;
padding: 0;
}


section.range-slider input::-webkit-slider-thumb {
pointer-events: all;
position: relative;
z-index: 1;
outline: 0;
}


section.range-slider input::-moz-range-thumb {
pointer-events: all;
position: relative;
z-index: 10;
-moz-appearance: none;
width: 9px;
}


section.range-slider input::-moz-range-track {
position: relative;
z-index: -1;
background-color: rgba(0, 0, 0, 1);
border: 0;
}
section.range-slider input:last-of-type::-moz-range-track {
-moz-appearance: none;
background: none transparent;
border: 0;
}
section.range-slider input[type=range]::-moz-focus-outer {
border: 0;
}
<!-- This block can be reused as many times as needed -->
<section class="range-slider">
<span class="rangeValues"></span>
<input value="5" min="0" max="15" step="0.5" type="range">
<input value="10" min="0" max="15" step="0.5" type="range">
</section>

虽然比较晚,但是 NoUiSlider避免了 jQuery-ui 依赖关系,而公认的答案没有这种依赖关系。它唯一的“警告”是 IE 支持 IE9和更新,如果遗留 IE 对你来说是一个交易破坏者。

它也是免费的,开源的,可以在商业项目中无限制地使用。

安装: 下载 noUiSlider,将 CSS 和 JS 文件解压缩到站点文件系统的某个地方,然后从 head 链接到 CSS,从 body 链接到 JS:

<!-- In <head> -->
<link href="nouislider.min.css" rel="stylesheet">


<!-- In <body> -->
<script src="nouislider.min.js"></script>

示例用法: 创建一个从0到100的滑块,并开始设置为20-80。

HTML:

<div id="slider">
</div>

约翰逊:

var slider = document.getElementById('slider');


noUiSlider.create(slider, {
start: [20, 80],
connect: true,
range: {
'min': 0,
'max': 100
}
});

当然,您可以简单地使用两个滑块相互叠加,并添加一点 javascript (实际上不超过5行) ,使选择器不超过最小/最大值(如@Garys)解决方案。

附上你会发现一个短片段改编自当前的项目,包括一些 CSS3样式,以显示你可以做什么(网络工具包只)。我还添加了一些标签来显示选定的值。

它使用 JQuery,但是 vanillajs 版本并不神奇。

@ Update: 下面的代码只是一个概念验证。由于许多请求,我为 MozillaFirefox 添加了一个可能的解决方案(不改变原始代码)。在使用下面的代码之前,您可能需要对其进行折射。

    (function() {


function addSeparator(nStr) {
nStr += '';
var x = nStr.split('.');
var x1 = x[0];
var x2 = x.length > 1 ? '.' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + '.' + '$2');
}
return x1 + x2;
}


function rangeInputChangeEventHandler(e){
var rangeGroup = $(this).attr('name'),
minBtn = $(this).parent().children('.min'),
maxBtn = $(this).parent().children('.max'),
range_min = $(this).parent().children('.range_min'),
range_max = $(this).parent().children('.range_max'),
minVal = parseInt($(minBtn).val()),
maxVal = parseInt($(maxBtn).val()),
origin = $(this).context.className;


if(origin === 'min' && minVal > maxVal-5){
$(minBtn).val(maxVal-5);
}
var minVal = parseInt($(minBtn).val());
$(range_min).html(addSeparator(minVal*1000) + ' €');




if(origin === 'max' && maxVal-5 < minVal){
$(maxBtn).val(5+ minVal);
}
var maxVal = parseInt($(maxBtn).val());
$(range_max).html(addSeparator(maxVal*1000) + ' €');
}


$('input[type="range"]').on( 'input', rangeInputChangeEventHandler);
})();
body{
font-family: sans-serif;
font-size:14px;
}
input[type='range'] {
width: 210px;
height: 30px;
overflow: hidden;
cursor: pointer;
outline: none;
}
input[type='range'],
input[type='range']::-webkit-slider-runnable-track,
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
background: none;
}
input[type='range']::-webkit-slider-runnable-track {
width: 200px;
height: 1px;
background: #003D7C;
}


input[type='range']:nth-child(2)::-webkit-slider-runnable-track{
background: none;
}


input[type='range']::-webkit-slider-thumb {
position: relative;
height: 15px;
width: 15px;
margin-top: -7px;
background: #fff;
border: 1px solid #003D7C;
border-radius: 25px;
z-index: 1;
}




input[type='range']:nth-child(1)::-webkit-slider-thumb{
z-index: 2;
}


.rangeslider{
position: relative;
height: 60px;
width: 210px;
display: inline-block;
margin-top: -5px;
margin-left: 20px;
}
.rangeslider input{
position: absolute;
}
.rangeslider{
position: absolute;
}


.rangeslider span{
position: absolute;
margin-top: 30px;
left: 0;
}


.rangeslider .right{
position: relative;
float: right;
margin-right: -5px;
}




/* Proof of concept for Firefox */
@-moz-document url-prefix() {
.rangeslider::before{
content:'';
width:100%;
height:2px;
background: #003D7C;
display:block;
position: relative;
top:16px;
}


input[type='range']:nth-child(1){
position:absolute;
top:35px !important;
overflow:visible !important;
height:0;
}


input[type='range']:nth-child(2){
position:absolute;
top:35px !important;
overflow:visible !important;
height:0;
}
input[type='range']::-moz-range-thumb {
position: relative;
height: 15px;
width: 15px;
margin-top: -7px;
background: #fff;
border: 1px solid #003D7C;
border-radius: 25px;
z-index: 1;
}


input[type='range']:nth-child(1)::-moz-range-thumb {
transform: translateY(-20px);
}
input[type='range']:nth-child(2)::-moz-range-thumb {
transform: translateY(-20px);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<div class="rangeslider">
<input class="min" name="range_1" type="range" min="1" max="100" value="10" />
<input class="max" name="range_1" type="range" min="1" max="100" value="90" />
<span class="range_min light left">10.000 €</span>
<span class="range_max light right">90.000 €</span>
</div>

实际上我直接在 html 中使用了我的脚本。但是在 javascript 中,当您为这个事件添加 oninput 事件侦听器时,它会自动提供数据。您只需要根据您的需求分配值。

[slider] {
width: 300px;
position: relative;
height: 5px;
margin: 45px 0 10px 0;
}


[slider] > div {
position: absolute;
left: 13px;
right: 15px;
height: 5px;
}
[slider] > div > [inverse-left] {
position: absolute;
left: 0;
height: 5px;
border-radius: 10px;
background-color: #CCC;
margin: 0 7px;
}


[slider] > div > [inverse-right] {
position: absolute;
right: 0;
height: 5px;
border-radius: 10px;
background-color: #CCC;
margin: 0 7px;
}




[slider] > div > [range] {
position: absolute;
left: 0;
height: 5px;
border-radius: 14px;
background-color: #d02128;
}


[slider] > div > [thumb] {
position: absolute;
top: -7px;
z-index: 2;
height: 20px;
width: 20px;
text-align: left;
margin-left: -11px;
cursor: pointer;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.4);
background-color: #FFF;
border-radius: 50%;
outline: none;
}


[slider] > input[type=range] {
position: absolute;
pointer-events: none;
-webkit-appearance: none;
z-index: 3;
height: 14px;
top: -2px;
width: 100%;
opacity: 0;
}


div[slider] > input[type=range]:focus::-webkit-slider-runnable-track {
background: transparent;
border: transparent;
}


div[slider] > input[type=range]:focus {
outline: none;
}


div[slider] > input[type=range]::-webkit-slider-thumb {
pointer-events: all;
width: 28px;
height: 28px;
border-radius: 0px;
border: 0 none;
background: red;
-webkit-appearance: none;
}


div[slider] > input[type=range]::-ms-fill-lower {
background: transparent;
border: 0 none;
}


div[slider] > input[type=range]::-ms-fill-upper {
background: transparent;
border: 0 none;
}


div[slider] > input[type=range]::-ms-tooltip {
display: none;
}


[slider] > div > [sign] {
opacity: 0;
position: absolute;
margin-left: -11px;
top: -39px;
z-index:3;
background-color: #d02128;
color: #fff;
width: 28px;
height: 28px;
border-radius: 28px;
-webkit-border-radius: 28px;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
text-align: center;
}


[slider] > div > [sign]:after {
position: absolute;
content: '';
left: 0;
border-radius: 16px;
top: 19px;
border-left: 14px solid transparent;
border-right: 14px solid transparent;
border-top-width: 16px;
border-top-style: solid;
border-top-color: #d02128;
}


[slider] > div > [sign] > span {
font-size: 12px;
font-weight: 700;
line-height: 28px;
}


[slider]:hover > div > [sign] {
opacity: 1;
}
<div slider id="slider-distance">
<div>
<div inverse-left style="width:70%;"></div>
<div inverse-right style="width:70%;"></div>
<div range style="left:0%;right:0%;"></div>
<span thumb style="left:0%;"></span>
<span thumb style="left:100%;"></span>
<div sign style="left:0%;">
<span id="value">0</span>
</div>
<div sign style="left:100%;">
<span id="value">100</span>
</div>
</div>
<input type="range" value="0" max="100" min="0" step="1" oninput="
this.value=Math.min(this.value,this.parentNode.childNodes[5].value-1);
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[1].style.width=value+'%';
children[5].style.left=value+'%';
children[7].style.left=value+'%';children[11].style.left=value+'%';
children[11].childNodes[1].innerHTML=this.value;" />


<input type="range" value="100" max="100" min="0" step="1" oninput="
this.value=Math.max(this.value,this.parentNode.childNodes[3].value-(-1));
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[3].style.width=(100-value)+'%';
children[5].style.right=(100-value)+'%';
children[9].style.left=value+'%';children[13].style.left=value+'%';
children[13].childNodes[1].innerHTML=this.value;" />
</div>

问题是: “有没有可能制作一个带有两个输入值的 HTML5滑块,例如选择一个价格范围?如果是这样,怎么才能做到呢?”

在2020年,有可能创建一个价格范围的 完全可访问的,本地的,非 jquery 的 HTML5滑块与两个拇指。如果发现这张贴后,我已经创建了这个解决方案,我认为这将是很好的分享我的实现在这里。

这个实现已经在移动版 Chrome 和 Firefox (Android)以及 Chrome 和 Firefox (Linux)上进行了测试。我不知道其他平台,但它应该是相当不错的。我希望得到您的反馈,并改进这个解决方案。

这个解决方案允许在一个页面上有多个实例,并且它只包含两个输入(每个) ,为屏幕阅读器提供描述性标签。可以在网格标签的数量中设置拇指大小。此外,您可以使用触摸,键盘和鼠标与滑块交互。由于“ on input”事件侦听器,该值在调整期间更新。

我的第一个方法是叠加和剪辑滑块。然而,这导致了复杂的代码和大量的浏览器依赖关系。然后我用两个“内联”的滑块重新创建了解决方案。这是您将在下面找到的解决方案。

var thumbsize = 14;


function draw(slider,splitvalue) {


/* set function vars */
var min = slider.querySelector('.min');
var max = slider.querySelector('.max');
var lower = slider.querySelector('.lower');
var upper = slider.querySelector('.upper');
var legend = slider.querySelector('.legend');
var thumbsize = parseInt(slider.getAttribute('data-thumbsize'));
var rangewidth = parseInt(slider.getAttribute('data-rangewidth'));
var rangemin = parseInt(slider.getAttribute('data-rangemin'));
var rangemax = parseInt(slider.getAttribute('data-rangemax'));


/* set min and max attributes */
min.setAttribute('max',splitvalue);
max.setAttribute('min',splitvalue);


/* set css */
min.style.width = parseInt(thumbsize + ((splitvalue - rangemin)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
max.style.width = parseInt(thumbsize + ((rangemax - splitvalue)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
min.style.left = '0px';
max.style.left = parseInt(min.style.width)+'px';
min.style.top = lower.offsetHeight+'px';
max.style.top = lower.offsetHeight+'px';
legend.style.marginTop = min.offsetHeight+'px';
slider.style.height = (lower.offsetHeight + min.offsetHeight + legend.offsetHeight)+'px';
    

/* correct for 1 off at the end */
if(max.value>(rangemax - 1)) max.setAttribute('data-value',rangemax);


/* write value and labels */
max.value = max.getAttribute('data-value');
min.value = min.getAttribute('data-value');
lower.innerHTML = min.getAttribute('data-value');
upper.innerHTML = max.getAttribute('data-value');


}


function init(slider) {
/* set function vars */
var min = slider.querySelector('.min');
var max = slider.querySelector('.max');
var rangemin = parseInt(min.getAttribute('min'));
var rangemax = parseInt(max.getAttribute('max'));
var avgvalue = (rangemin + rangemax)/2;
var legendnum = slider.getAttribute('data-legendnum');


/* set data-values */
min.setAttribute('data-value',rangemin);
max.setAttribute('data-value',rangemax);
    

/* set data vars */
slider.setAttribute('data-rangemin',rangemin);
slider.setAttribute('data-rangemax',rangemax);
slider.setAttribute('data-thumbsize',thumbsize);
slider.setAttribute('data-rangewidth',slider.offsetWidth);


/* write labels */
var lower = document.createElement('span');
var upper = document.createElement('span');
lower.classList.add('lower','value');
upper.classList.add('upper','value');
lower.appendChild(document.createTextNode(rangemin));
upper.appendChild(document.createTextNode(rangemax));
slider.insertBefore(lower,min.previousElementSibling);
slider.insertBefore(upper,min.previousElementSibling);
    

/* write legend */
var legend = document.createElement('div');
legend.classList.add('legend');
var legendvalues = [];
for (var i = 0; i < legendnum; i++) {
legendvalues[i] = document.createElement('div');
var val = Math.round(rangemin+(i/(legendnum-1))*(rangemax - rangemin));
legendvalues[i].appendChild(document.createTextNode(val));
legend.appendChild(legendvalues[i]);


}
slider.appendChild(legend);


/* draw */
draw(slider,avgvalue);


/* events */
min.addEventListener("input", function() {update(min);});
max.addEventListener("input", function() {update(max);});
}


function update(el){
/* set function vars */
var slider = el.parentElement;
var min = slider.querySelector('#min');
var max = slider.querySelector('#max');
var minvalue = Math.floor(min.value);
var maxvalue = Math.floor(max.value);
    

/* set inactive values before draw */
min.setAttribute('data-value',minvalue);
max.setAttribute('data-value',maxvalue);


var avgvalue = (minvalue + maxvalue)/2;


/* draw */
draw(slider,avgvalue);
}


var sliders = document.querySelectorAll('.min-max-slider');
sliders.forEach( function(slider) {
init(slider);
});
* {padding: 0; margin: 0;}
body {padding: 40px;}


.min-max-slider {position: relative; width: 200px; text-align: center; margin-bottom: 50px;}
.min-max-slider > label {display: none;}
span.value {height: 1.7em; font-weight: bold; display: inline-block;}
span.value.lower::before {content: "€"; display: inline-block;}
span.value.upper::before {content: "- €"; display: inline-block; margin-left: 0.4em;}
.min-max-slider > .legend {display: flex; justify-content: space-between;}
.min-max-slider > .legend > * {font-size: small; opacity: 0.25;}
.min-max-slider > input {cursor: pointer; position: absolute;}


/* webkit specific styling */
.min-max-slider > input {
-webkit-appearance: none;
outline: none!important;
background: transparent;
background-image: linear-gradient(to bottom, transparent 0%, transparent 30%, silver 30%, silver 60%, transparent 60%, transparent 100%);
}
.min-max-slider > input::-webkit-slider-thumb {
-webkit-appearance: none; /* Override default look */
appearance: none;
width: 14px; /* Set a specific slider handle width */
height: 14px; /* Slider handle height */
background: #eee; /* Green background */
cursor: pointer; /* Cursor on hover */
border: 1px solid gray;
border-radius: 100%;
}
.min-max-slider > input::-webkit-slider-runnable-track {cursor: pointer;}
<div class="min-max-slider" data-legendnum="2">
<label for="min">Minimum price</label>
<input id="min" class="min" name="min" type="range" step="1" min="0" max="3000" />
<label for="max">Maximum price</label>
<input id="max" class="max" name="max" type="range" step="1" min="0" max="3000" />
</div>

注意,您应该将步骤大小保持为1,以防止由于重绘/重绘错误而改变值。

在线观看: https://codepen.io/joosts/pen/rNLdxvK

此代码涵盖以下几点

  1. 使用 HTML,CSS,JS 的双滑块

  2. 我已经修改了这个滑块使用嵌入式红宝石,所以我们可以保存以前应用的价值使用参数在轨道。

     <% left_width = params[:min].nil? ? 0 : ((params[:min].to_f/100000) * 100).to_i %>
    <% left_value = params[:min].nil? ? '0' : params[:min] %>
    <% right_width = params[:max].nil? ? 100 : ((params[:max].to_f/100000) * 100).to_i %>
    <% right_value = params[:max].nil? ? '100000' : params[:max] %>
    
    
    <div class="range-slider-outer">
    <div slider id="slider-distance">
    <div class="slider-inner">
    <div inverse-left style="width:<%= left_width %>%;"></div>
    <div inverse-right style="width:<%= 100 - right_width %>%;"></div>
    <div range style="left:<%= left_width %>%;right:<%= 100 - right_width %>%;"></div>
    <span thumb style="left:<%= left_width %>%;"></span>
    <span thumb style="left:<%= right_width %>%;"></span>
    <div sign style="">
    Rs.<span id="value"><%= left_value.to_i %></span> to
    </div>
    <div sign style="">
    Rs.<span id="value"><%= right_value.to_i %></span>
    </div>
    </div>
    
    
    <input type="range" name="min" value=<%= left_value %> max="100000" min="0" step="100" oninput="
    this.value=Math.min(this.value,this.parentNode.childNodes[5].value-1);
    let value = (this.value/parseInt(this.max))*100
    var children = this.parentNode.childNodes[1].childNodes;
    children[1].style.width=value+'%';
    children[5].style.left=value+'%';
    children[7].style.left=value+'%';children[11].style.left=value+'%';
    children[11].childNodes[1].innerHTML=this.value;" />
    
    
    <input type="range" name="max" value=<%= right_value %> max="100000" min="0" step="100" oninput="
    this.value=Math.max(this.value,this.parentNode.childNodes[3].value-(-1));
    let value = (this.value/parseInt(this.max))*100
    var children = this.parentNode.childNodes[1].childNodes;
    children[3].style.width=(100-value)+'%';
    children[5].style.right=(100-value)+'%';
    children[9].style.left=value+'%';children[13].style.left=value+'%';
    children[13].childNodes[1].innerHTML=this.value;" />
    </div>
    <div class="range-label">
    <div>0</div>
    <div>100000</div>
    </div>
    </div>
    

[slider] {
/*width: 300px;*/
position: relative;
height: 5px;
/*margin: 20px auto;*/
/* height: 100%; */
}
[slider] > div {
position: absolute;
left: 13px;
right: 15px;
height: 14px;
top: 5px;
}
[slider] > div > [inverse-left] {
position: absolute;
left: 0;
height: 14px;
border-radius: 3px;
background-color: #CCC;
/*margin: 0 7px;*/
margin: 0 -7px;
}
[slider] > div > [inverse-right] {
position: absolute;
right: 0;
height: 14px;
border-radius: 3px;
background-color: #CCC;
/*margin: 0 7px;*/
margin: 0 -7px;
}
[slider] > div > [range] {
position: absolute;
left: 0;
height: 14px;
border-radius: 14px;
background-color:#8950fc;
}
[slider] > div > [thumb] {
position: absolute;
top: -3px;
z-index: 2;
height: 20px;
width: 20px;
text-align: left;
margin-left: -11px;
cursor: pointer;
/* box-shadow: 0 3px 8px rgba(0, 0, 0, 0.4); */
background-color: #FFF;
/*border-radius: 50%;*/
border-radius:2px;
outline: none;
}
[slider] > input[type=range] {
position: absolute;
pointer-events: none;
-webkit-appearance: none;
z-index: 3;
height: 14px;
top: -2px;
width: 100%;
opacity: 0;
}
div[slider] > input[type=range]:focus::-webkit-slider-runnable-track {
background: transparent;
border: transparent;
}
div[slider] > input[type=range]:focus {
outline: none;
}
div[slider] > input[type=range]::-webkit-slider-thumb {
pointer-events: all;
width: 28px;
height: 28px;
border-radius: 0px;
border: 0 none;
background: red;
-webkit-appearance: none;
}
div[slider] > input[type=range]::-ms-fill-lower {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-fill-upper {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-tooltip {
display: none;
}
[slider] > div > [sign] {
/* opacity: 0;
position: absolute;
margin-left: -11px;
top: -39px;
z-index:3;
background-color:#1a243a;
color: #fff;
width: 28px;
height: 28px;
border-radius: 28px;
-webkit-border-radius: 28px;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
text-align: center;*/
color: #A5B2CB;
border-radius: 28px;
justify-content: center;
text-align: center;
display: inline-block;
margin-top: 12px;
font-size: 14px;
font-weight: bold;
}
.slider-inner{
text-align:center;
}
/*[slider] > div > [sign]:after {
position: absolute;
content: '';
left: 0;
border-radius: 16px;
top: 19px;
border-left: 14px solid transparent;
border-right: 14px solid transparent;
border-top-width: 16px;
border-top-style: solid;
border-top-color:#1a243a;
}*/
[slider] > div > [sign] > span {
font-size: 12px;
font-weight: 700;
line-height: 28px;
}
[slider]:hover > div > [sign] {
opacity: 1;
}
.range-label{
display: flex;
justify-content: space-between;
margin-top: 28px;
padding: 0px 5px;
}
.range-slider-outer{
width:calc(100% - 20px);
margin:auto;
margin-bottom: 10px;
margin-top: 10px;
}

2022年——无障碍解决方案——实施30秒解决方案

此解决方案基于 这个问题的答案是@JoostS构建。可访问性是没有一个答案关注的问题,所以我在上面的答案的基础上增加了可访问性和可扩展性,因为它有一些缺陷。

用法很简单:

  1. 使用 CDN或在本地托管脚本: https://cdn.jsdelivr.net/gh/maxshuty/accessible-web-components/dist/simpleRange.min.js
  2. 将此元素添加到模板或 HTML: <range-selector min-range="0" max-range="1000" />
  3. 通过监听 range-changed事件(或者你传入的任何 event-name-to-emit-on-change事件)来连接它

就是这样。点击这里查看完整的演示.您可以很容易地自定义它,只需应用属性,如 inputs-for-labels使用输入而不是标签,slider-color调整颜色,等等!

这是一把小提琴:

window.addEventListener('range-changed', (e) => {console.log(`Range changed for: ${e.detail.sliderId}. Min/Max range values are available in this object too`)})
<script src="https://cdn.jsdelivr.net/gh/maxshuty/accessible-web-components@latest/dist/simpleRange.min.js"></script>


<div>
<range-selector
id="rangeSelector1"
min-label="Minimum"
max-label="Maximum"
min-range="1000"
max-range="2022"
number-of-legend-items-to-show="6"
/>
</div>


<div>
<range-selector
id="rangeSelector1"
min-label="Minimum"
max-label="Maximum"
min-range="1"
max-range="500"
number-of-legend-items-to-show="3"
inputs-for-labels
/>
</div>


<div>
<range-selector
id="rangeSelector2"
min-label="Minimum"
max-label="Maximum"
min-range="1000"
max-range="2022"
number-of-legend-items-to-show="3"
slider-color="#6b5b95"
/>
</div>


<div>
<range-selector
id="rangeSelector3"
min-label="Minimum"
max-label="Maximum"
min-range="1000"
max-range="2022"
hide-label
hide-legend
/>
</div>

我决定使用 display: none(不适合 a11y)来解决像标签这样的链接答案问题,在滑块上没有可视化焦点,等等,并且通过清理事件侦听器来改进代码,使其更加动态和可扩展。

我创建了这个 库与许多选项来定制颜色,事件名称,很容易挂接到它,使易访问的标签 i18n 能够和更多。如果你想玩的话。

您可以轻松地自定义它显示的图例项的数量,隐藏或显示标签和图例,并自定义所有东西的颜色,包括像这样的焦点颜色。

使用几个道具的例子:

<range-selector
min-label="i18n Minimum Range"
max-label="i18n Maximum Range"
min-range="5"
max-range="555"
number-of-legend-items-to-show="6"
event-name-to-emit-on-change="my-custom-range-changed-event"
slider-color="orange"
circle-color="#f7cac9"
circle-border-color="#083535"
circle-focus-border-color="#3ec400"
/>

然后在你的剧本里:

window.addEventListener('my-custom-range-changed-event', (e) => { const data = e.detail; });

最后,如果您发现这里缺少了一些您需要的东西,我可以很容易地定制这个库。

只需复制 这份文件,在顶部您可以看到 cssHelpersconstants对象,其中包含您可能希望进一步自定义的大多数变量。

自从我使用 本机 Web 组件构建了这个应用程序以来,我已经利用了 disconnectedCallback和其他钩子来清理事件侦听器并进行设置。

这里是一个可重用的双范围滑块实现,基于教程 编码艺术家的双范围滑块

  • 接近原生用户界面,兼容 Chrome/Firefox/Safari
  • 基于 API EventTarget,带有 change/input 事件,minGap/maxGap 属性

let $ = (s, c = document) => c.querySelector(s);
let $$ = (s, c = document) => Array.prototype.slice.call(c.querySelectorAll(s));


class DoubleRangeSlider extends EventTarget {
#minGap = 0;
#maxGap = Number.MAX_SAFE_INTEGER;
#inputs;
style = {
trackColor: '#dadae5',
rangeColor: '#3264fe',
};
constructor(container){
super();
let inputs = $$('input[type="range"]', container);
if(inputs.length !== 2){
throw new RangeError('2 range inputs expected');
}
let [input1, input2] = inputs;
if(input1.min >= input1.max || input2.min >= input2.max){
throw new RangeError('range min should be less than max');
}
if(input1.max > input2.max || input1.min > input2.min){
throw new RangeError('input1\'s max/min should not be greater than input2\'s max/min');
}
this.#inputs = inputs;
let sliderTrack = $('.slider-track', container);
let lastValue1 = input1.value;
input1.addEventListener('input', (e) => {
let value1 = +input1.value;
let value2 = +input2.value;
let minGap = this.#minGap;
let maxGap = this.#maxGap;
let gap = value2 - value1;
let newValue1 = value1;
if(gap < minGap){
newValue1 = value2 - minGap;
}else if(gap > maxGap){
newValue1 = value2 - maxGap;
}
input1.value = newValue1;
if(input1.value !== lastValue1){
lastValue1 = input1.value;
passEvent(e);
fillColor();
}
});
let lastValue2 = input2.value;
input2.addEventListener('input', (e) => {
let value1 = +input1.value;
let value2 = +input2.value;
let minGap = this.#minGap;
let maxGap = this.#maxGap;
let gap = value2 - value1;
let newValue2 = value2;
if(gap < minGap){
newValue2 = value1 + minGap;
}else if(gap > maxGap){
newValue2 = value1 + maxGap;
}
input2.value = newValue2;
if(input2.value !== lastValue2){
lastValue2 = input2.value;
passEvent(e);
fillColor();
}
});
let passEvent = (e) => {
this.dispatchEvent(new e.constructor(e.type, e));
};
input1.addEventListener('change', passEvent);
input2.addEventListener('change', passEvent);
let fillColor = () => {
let overallMax = +input2.max;
let overallMin = +input1.min;
let overallRange = overallMax - overallMin;
let left1 = ((input1.value - overallMin) / overallRange * 100) + '%';
let left2 = ((input2.value - overallMin) / overallRange * 100) + '%';
let {trackColor, rangeColor} = this.style;
sliderTrack.style.background = `linear-gradient(to right, ${trackColor} ${left1}, ${rangeColor} ${left1}, ${rangeColor} ${left2}, ${trackColor} ${left2})`;
};
let init = () => {
let overallMax = +input2.max;
let overallMin = +input1.min;
let overallRange = overallMax - overallMin;
let range1 = input1.max - overallMin;
let range2 = overallMax - input2.min;
input1.style.left = '0px';
input1.style.width = (range1 / overallRange * 100) + '%';
input2.style.right = '0px';
input2.style.width = (range2 / overallRange * 100) + '%';
fillColor();
};
init();
}
get minGap(){
return this.#minGap;
}
set minGap(v){
this.#minGap = v;
}
get maxGap(){
return this.#maxGap;
}
set maxGap(v){
this.#maxGap = v;
}
get values(){
return this.#inputs.map((el) => el.value);
}
set values(values){
if(values.length !== 2 || !values.every(isFinite))
throw new RangeError();
let [input1, input2] = this.#inputs;
let [value1, value2] = values;
if(value1 > input1.max || value1 < input1.min)
throw new RangeError('invalid value for input1');
if(value2 > input2.max || value2 < input2.min)
throw new RangeError('invalid value for input2');
input1.value = value1;
input2.value = value2;
}
get inputs(){
return this.#inputs;
}
get overallMin(){
return this.#inputs[0].min;
}
get overallMax(){
return this.#inputs[1].max;
}
}




function main(){
let container = $('.slider-container');
let slider = new DoubleRangeSlider(container);
slider.minGap = 30;
slider.maxGap = 70;


let inputs = $$('input[name="a"]');
let outputs = $$('output[name="a"]');
outputs[0].value = inputs[0].value;
outputs[1].value = inputs[1].value;


slider.addEventListener('input', (e) => {
let values = slider.values;
outputs[0].value = values[0];
outputs[1].value = values[1];
});
slider.addEventListener('change', (e) => {
let values = slider.values;
console.log('change', values);
outputs[0].value = values[0];
outputs[1].value = values[1];
});
}
document.addEventListener('DOMContentLoaded', main);
.slider-container {
display: inline-block;
position: relative;
width: 360px;
height: 28px;
}
.slider-track {
width: 100%;
height: 5px;
position: absolute;
margin: auto;
top: 0;
bottom: 0;
border-radius: 5px;
}
.slider-container>input[type="range"] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
position: absolute;
margin: auto;
top: 0;
bottom: 0;
width: 100%;
outline: none;
background-color: transparent;
pointer-events: none;
}
.slider-container>input[type="range"]::-webkit-slider-runnable-track {
-webkit-appearance: none;
height: 5px;
}
.slider-container>input[type="range"]::-moz-range-track {
-moz-appearance: none;
height: 5px;
}
.slider-container>input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
margin-top: -9px;
height: 1.7em;
width: 1.7em;
background-color: #3264fe;
cursor: pointer;
pointer-events: auto;
border-radius: 50%;
}
.slider-container>input[type="range"]::-moz-range-thumb {
-moz-appearance: none;
height: 1.7em;
width: 1.7em;
cursor: pointer;
border: none;
border-radius: 50%;
background-color: #3264fe;
pointer-events: auto;
}
.slider-container>input[type="range"]:active::-webkit-slider-thumb {
background-color: #ffffff;
border: 3px solid #3264fe;
}
<h3>Double Range Slider, Reusable Edition</h3>


<div class="slider-container">
<div class="slider-track"></div>
<input type="range" name="a" min="-130" max="-30" step="1" value="-100" autocomplete="off" />
<input type="range" name="a" min="-60" max="0" step="2" value="-30" autocomplete="off" />
</div>


<div>
<output name="a"></output> ~ <output name="a"></output>
</div>


<pre>
Changes:
1. allow different min/max/step for two inputs
2. new property 'maxGap'
3. added events 'input'/'change'
4. dropped IE/OldEdge support
</pre>

对于那些使用 Vue 的人来说,现在有基于 noUiSlider 的 再见可用。但它似乎不再被维护。:-(