Monday, May 20, 2024
 Popular · Latest · Hot · Upcoming
142
rated 0 times [  146] [ 4]  / answers: 1 / hits: 8256  / 4 Years ago, tue, december 1, 2020, 12:00:00

I have an almost working solution in the sandbox linked in the update below, you should start with this.




I have one that's created without using React (Click "Run Code Snippet")




var thumbsize = 14;

function draw(slider,splitvalue) {

/* set function vars */
var min = slider.querySelector('.min');
var max = slider.querySelector('.max');
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';

/* 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');

}

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;

/* 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);

/* 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);
});

.min-max-slider {
position: relative;
width: 200px;
text-align: center;
margin-bottom: 50px;
}

.min-max-slider > label {
display: none;
}

.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>




I've tried to create the same sort of component using React. But each thumbgrip can only slide to the mid-point, where as with the non-react example, the lesser slider simply can't be higher than the higher slider and vice versa.


I would also like some way to make the slider which outside the range of the thumbgrips a darker color.




var thumbsize = 14;

const Slider = ({min, max}) => {
const avg = (min + max)/2;
const width = 300
const minWidth = thumbsize + ((avg - min)/(max - min))*(width - (2*thumbsize))
const styles={
min:{
width: minWidth,
left: 0
},
max:{
width: thumbsize + ((max - avg)/(max - min))*(width - (2*thumbsize)),
left: minWidth
}
}

return (
<div
class=min-max-slider
data-legendnum=2
data-rangemin={min}
data-rangemax={max}
data-thumbsize={thumbsize}
data-rangewidth={width}
>
<label htmlFor=min>Minimum price</label>
<input
id=min
className=min
style={styles.min}
name=min
type=range
step=1
min={min}
max={avg}
data-value={min}
/>
<label htmlFor=max>Maximum price</label>
<input
id=max
className=max
style={styles.max}
name=max
type=range
step=1
min={avg}
max={max}
data-value={max}
/>
</div>
)
}

ReactDOM.render(<Slider min={300} max={3000} />, document.querySelector(#root))

 



.min-max-slider {
position: relative;
width: 200px;
text-align: center;
margin-bottom: 50px;
}

.min-max-slider > label {
display: none;
}

.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;}

<script src=https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js></script>
<div id=root></div>






UPDATE


Using lissetdm's solution below, I have been able to create a double sided slider, which you can view on this sandbox, but there are still a few problems with it.



  1. The orange doesn't always look like it overlaps the gray


enter


Sometimes, unpredictably, this difference is very noticeable


enter
enter



  1. When both thumb pieces are dragged to the edges, the one pushes the other down


enter



  1. I can't seem to remove this outline, which doesn't always appear


enter




I have tested this out in firefox


More From » reactjs

 Answers
6

As react works, you need to move avg to the component's state and during input onChange event update it's value:


const Slider = ({ min, max }) => {
const [avg, setAvg] = useState((min + max) / 2);
const [minVal, setMinVal] = useState(avg);
const [maxVal, setMaxVal] = useState(avg);

const width = 300;
const minWidth =
thumbsize + ((avg - min) / (max - min)) * (width - 2 * thumbsize);
const styles = {
min: {
width: minWidth,
left: 0
},
max: {
width: thumbsize + ((max - avg) / (max - min)) * (width - 2 * thumbsize),
left: minWidth
}
};

useEffect(() => {
setAvg((maxVal + minVal) / 2);
}, [minVal, maxVal]);


return (
<div
className="min-max-slider"
data-legendnum="2"
data-rangemin={min}
data-rangemax={max}
data-thumbsize={thumbsize}
data-rangewidth={width}
>
<label htmlFor="min">Minimum price</label>
<input
id="min"
className="min"
style={styles.min}
name="min"
type="range"
step="1"
min={min}
max={avg}
value={minVal}
onChange={({ target }) => setMinVal(Number(target.value))}
/>
<label htmlFor="max">Maximum price</label>
<input
id="max"
className="max"
style={styles.max}
name="max"
type="range"
step="1"
min={avg}
max={max}
value={maxVal}
onChange={({ target }) => setMaxVal(Number(target.value))}
/>
</div>
);
};

UPDATED



I can't seem to remove this outline, which doesn't always appear



This line appears because the browser detects that the input is invalid. To disable this effect use:


input[type="range"]:invalid {
box-shadow: none;
}


The orange doesn't always look like it overlaps the gray



And



When both thumb pieces are dragged to the edges, the one pushes the
other down



I can't reproduce this scenarios but I recommend you to control the background color using the min and max position:


CSS:


input[type="range"] {
--minRangePercent: 0%;
--maxRangePercent: 0%;
height: .4rem;
}

.min-max-slider > input.min {
background-image: linear-gradient(
to right,
silver 0%,
silver var(--minRangePercent),
orange var(--minRangePercent),
orange 100%
);
}

.min-max-slider > input.max {
background-image: linear-gradient(
to right,
orange 0%,
orange var(--maxRangePercent),
silver var(--maxRangePercent),
silver 100%
);
}

JS:


const minPercent = ((minVal - min) / (avg - min)) * 100;
const maxPercent = ((maxVal - avg) / (max - avg)) * 100;
const styles = {
min: {
width: minWidth,
left: 0,
"--minRangePercent": `${minPercent}%`
},
max: {
width: thumbsize + ((max - avg) / (max - min)) * (width - 2 * thumbsize),
left: minWidth,
"--maxRangePercent": `${maxPercent}%`
}
};

Working example


[#2191] Thursday, November 26, 2020, 4 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
tobyl

Total Points: 598
Total Questions: 110
Total Answers: 114

Location: Vietnam
Member since Sat, Feb 12, 2022
2 Years ago
;