React JS中Slider组件如何正确设置宽度填充外部Div?
多范围滑块宽度设为100%后Thumb位置异常的解决方法
我基于React实现了多范围滑块,原本示例中.slider和.thumb的宽度为200px,现在需要将滑块放入grid容器中并占满容器宽度。但修改宽度为100%后,左侧thumb移出屏幕,右侧thumb偏右过多。
复现代码
App.js
import MultiRangeSlider from "./component/multiRangeSlider/MultiRangeSlider"; import "./styles.css"; const App = () => { return ( <div className="grid-container"> <div className="main1"> <MultiRangeSlider min={0} max={1000} onChange={({ min, max }) => console.log(`min = ${min}, max = ${max}`)} /> </div> <div className="main2"> <p>main 2</p> </div> </div> ); }; export default App;
styles.css
* { box-sizing: border-box; margin: 0; padding: 0; } body { background-color: #242526; font-family: sans-serif; } .grid-container { display: grid; grid-template-areas: "main1 main2"; grid-template-columns: 1fr 1fr; gap: 40px; margin: auto; margin-bottom: 40px; } .main1 {grid-area: main1;} .main2 {grid-area: main2;}
MultiRangeSlider.css(出现异常的修改版本)
.container { height: 100vh; display: flex; align-items: center; justify-content: center; } .slider { position: relative; width: 100%; } .slider__track, .slider__range, .slider__left-value, .slider__right-value { position: absolute; } .slider__track, .slider__range { border-radius: 3px; height: 5px; } .slider__track { background-color: #ced4da; width: 100%; z-index: 1; } .slider__range { background-color: #9fe5e1; z-index: 2; } .slider__left-value, .slider__right-value { color: #dee2e6; font-size: 12px; margin-top: 20px; } .slider__left-value { left: 6px; } .slider__right-value { right: -4px; } /* Removing the default appearance */ .thumb, .thumb::-webkit-slider-thumb { -webkit-appearance: none; -webkit-tap-highlight-color: transparent; } .thumb { pointer-events: none; position: absolute; height: 0; width: 100%; outline: none; } .thumb--left { z-index: 3; } .thumb--right { z-index: 4; } /* For Chrome browsers */ .thumb::-webkit-slider-thumb { background-color: #f1f5f7; border: none; border-radius: 50%; box-shadow: 0 0 1px 1px #ced4da; cursor: pointer; height: 18px; width: 18px; margin-top: 4px; pointer-events: all; position: relative; } /* For Firefox browsers */ .thumb::-moz-range-thumb { background-color: #f1f5f7; border: none; border-radius: 50%; box-shadow: 0 0 1px 1px #ced4da; cursor: pointer; height: 18px; width: 18px; margin-top: 4px; pointer-events: all; position: relative; }
MultiRangeSlider.js(未修改)
import React, { useCallback, useEffect, useState, useRef } from "react"; import PropTypes from "prop-types"; import "./multiRangeSlider.css"; const MultiRangeSlider = ({ min, max, onChange }) => { const [minVal, setMinVal] = useState(min); const [maxVal, setMaxVal] = useState(max); const minValRef = useRef(min); const maxValRef = useRef(max); const range = useRef(null); // Convert to percentage const getPercent = useCallback( (value) => Math.round(((value - min) / (max - min)) * 100), [min, max] ); // Set width of the range to decrease from the left side useEffect(() => { const minPercent = getPercent(minVal); const maxPercent = getPercent(maxValRef.current); if (range.current) { range.current.style.left = `${minPercent}%`; range.current.style.width = `${maxPercent - minPercent}%`; } }, [minVal, getPercent]); // Set width of the range to decrease from the right side useEffect(() => { const minPercent = getPercent(minValRef.current); const maxPercent = getPercent(maxVal); if (range.current) { range.current.style.width = `${maxPercent - minPercent}%`; } }, [maxVal, getPercent]); // Get min and max values when their state changes useEffect(() => { onChange({ min: minVal, max: maxVal }); }, [minVal, maxVal, onChange]); return ( <div className="container"> <input type="range" min={min} max={max} value={minVal} onChange={(event) => { const value = Math.min(Number(event.target.value), maxVal - 1); setMinVal(value); minValRef.current = value; }} className="thumb thumb--left" style={{ zIndex: minVal > max - 100 && "5" }} /> <input type="range" min={min} max={max} value={maxVal} onChange={(event) => { const value = Math.max(Number(event.target.value), minVal + 1); setMaxVal(value); maxValRef.current = value; }} className="thumb thumb--right" /> <div className="slider"> <div className="slider__track" /> <div ref={range} className="slider__range" /> <div className="slider__left-value">{minVal}</div> <div className="slider__right-value">{maxVal}</div> </div> </div> ); }; MultiRangeSlider.propTypes = { min: PropTypes.number.isRequired, max: PropTypes.number.isRequired, onChange: PropTypes.func.isRequired }; export default MultiRangeSlider;
问题原因及解决方法
原因
问题源于原生range输入框的默认边距、.container的居中布局,以及thumb的垂直定位偏移。当宽度改为100%后,这些默认样式导致滑块位置计算出现偏差。
解决步骤
- 调整
.container布局:移除不必要的全屏高度和居中设置,让滑块适应grid容器:
.container { display: flex; align-items: center; /* 移除 height: 100vh 和 justify-content: center */ }
- 抵消原生range的默认边距:给
.thumb添加左右内边距,值为thumb宽度的一半(18px/2=9px),确保拖动范围与视觉轨道对齐:
.thumb { pointer-events: none; position: absolute; height: 0; width: 100%; outline: none; padding: 0 9px; box-sizing: border-box; }
- 修正thumb垂直定位:调整
margin-top让thumb与轨道垂直居中,同时移除多余的相对定位:
/* Chrome */ .thumb::-webkit-slider-thumb { /* 其他样式不变 */ margin-top: -7px; /* 移除 position: relative */ } /* Firefox */ .thumb::-moz-range-thumb { /* 其他样式不变 */ margin-top: -7px; /* 移除 position: relative */ }
- 调整数值标签位置:让标签与滑块对齐,避免偏移:
.slider__left-value { left: 0; } .slider__right-value { right: 0; }
内容的提问来源于stack exchange,提问作者Sarah
相关产品推荐
相关产品推荐

