双拇指区间滑块限制最值不交叉的JavaScript实现方案
双拇指范围滑块越界修复方案
现有代码存在3个问题导致拖动越界:
- 仅对最小值做了不超过最大值的单边校验,未对最大值做「不低于最小值」的反向限制
- 校验得到的合法值没有回写到滑块输入框,导致滑块视觉位置停在越界位置
- 没有同步更新关联的数字输入框值,也未做数字输入框和滑块的双向绑定,手动输入数字时不会触发边界校验
修复逻辑
- 对两个滑块值做双向边界限制:最小值永远不超过当前最大值,最大值永远不低于当前最小值
- 校验完成后把合法值回写到滑块输入框,避免滑块停在越界位置
- 同步滑块和数字输入框的值,手动输入数字时同样触发边界校验
- 动态更新数字输入框的max/min属性,从原生输入层面拦截非法值
修复后完整JS代码
const twoRangeSlider = (() => { const rangeCheck = (rangeInputs, rangeMinOutput, rangeMaxOutput) => { const rangeMin = Number(rangeInputs[0].min || 0); const rangeMax = Number(rangeInputs[0].max || 100); let rangeMinValue = parseFloat(rangeInputs[1].value); let rangeMaxValue = parseFloat(rangeInputs[0].value); // 双向边界校验 rangeMinValue = Math.min(Math.max(rangeMinValue, rangeMin), rangeMaxValue); rangeMaxValue = Math.max(Math.min(rangeMaxValue, rangeMax), rangeMinValue); // 回写修正后的值到滑块,避免越界显示 rangeInputs[1].value = rangeMinValue; rangeInputs[0].value = rangeMaxValue; // 同步数字输入框显示 rangeMinOutput.value = rangeMinValue; rangeMaxOutput.value = rangeMaxValue; // 计算轨道填充百分比 const rangeMinPercentage = ((rangeMinValue - rangeMin) * 100) / (rangeMax - rangeMin); const rangeMaxPercentage = ((rangeMaxValue - rangeMin) * 100) / (rangeMax - rangeMin); // 更新轨道样式 rangeInputs[0].style.background = `linear-gradient(to right, #000 ${rangeMaxPercentage}%, #c4c8ca ${rangeMaxPercentage}%)`; rangeInputs[1].style.background = `linear-gradient(to right,#c4c8ca ${rangeMinPercentage}%, transparent ${rangeMinPercentage}%)`; }; const bindComponent = (item) => { const rangeInputs = item.querySelectorAll('.js-two-range-slider-input'); const rangeMinOutput = item.querySelector('.js-two-range-slider-min-value'); const rangeMaxOutput = item.querySelector('.js-two-range-slider-max-value'); // 监听滑块拖动事件 item.addEventListener("input", (e) => { // 动态更新数字输入框的边界限制 if(e.target === rangeInputs[1]) rangeMaxOutput.min = e.target.value; if(e.target === rangeInputs[0]) rangeMinOutput.max = e.target.value; rangeCheck(rangeInputs, rangeMinOutput, rangeMaxOutput); }); // 监听数字输入框手动输入事件 rangeMinOutput.addEventListener("input", () => { rangeInputs[1].value = rangeMinOutput.value; rangeMaxOutput.min = rangeMinOutput.value; rangeCheck(rangeInputs, rangeMinOutput, rangeMaxOutput); }) rangeMaxOutput.addEventListener("input", () => { rangeInputs[0].value = rangeMaxOutput.value; rangeMinOutput.max = rangeMaxOutput.value; rangeCheck(rangeInputs, rangeMinOutput, rangeMaxOutput); }) // 初始化状态 rangeMaxOutput.min = rangeMinOutput.value; rangeMinOutput.max = rangeMaxOutput.value; rangeCheck(rangeInputs, rangeMinOutput, rangeMaxOutput); }; const init = () => { const rootEl = document.getElementsByClassName("js-two-range-slider"); [...rootEl].forEach((item) => bindComponent(item)); }; return { init }; })(); twoRangeSlider.init();
原有CSS和HTML代码无需修改,直接替换JS即可实现效果:
- 最小值滑块拖到最大值滑块位置时自动拦截,无法继续向右越界
- 最大值滑块拖到最小值滑块位置时自动拦截,无法继续向左越界
- 手动修改下方数字输入框数值时,同样触发边界校验,不会出现最小值大于最大值的情况
- 滑块轨道填充效果和数值完全同步
内容的提问来源于stack exchange,提问作者TheCodingTentacle
相关产品推荐
相关产品推荐

