Reputation: 1781
I am building a slider component in React JS, and it's working, however I am facing a bug which I believe to be a conflict between onMouseDown
and onMouseLeave
events.
I have the div range-container
, which receives the events, inside it I have another div and within this last one I have two spans, they are the thumbs of my slider.
This is what is happening:
As seen in this gif the thumbs don't respect the limits of the line as they should. The data on the left are the variable move
, responsable to determine whether the X
can be changed or not and the position of the mouse.
This is how it should work:
onMouseDown
sets move
to true
and allows the thumb to move;
onMouseUp
sets move
as false
and blocks the movements;
onMouseMove
changes the value of value
and makes the thumb moves;
onMouseLeave
sets move
to false
and also blocks the movements.
I realize that onMouseLeave
is only triggered when the cursor leaves the element and its children, because of that I can't just leave the div, I need to leave the thumb as well, but I don't know how to limit it by the limits of the line.
Here is my component:
import React, { Fragment } from 'react'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import './Filter.css'
const Filter = props => {
let [value, setValue] = React.useState(190)
let [move, setMove] = React.useState(false)
const handleChange = e => {
var x = e.clientX;
if (move === true) {
setValue(x)
}
};
const moveOn = e => {
var x = e.clientX;
setValue(x)
setMove(true)
}
const moveOff = () => {
setMove(false)
}
let moveText = 'false'
move === true ? moveText = 'true' : moveText = 'false'
return (
<Fragment>
<div>{moveText}</div>
<div>{value}</div>
<div className="filter-container d-flex justify-content-between">
<div className="range-container"
onMouseMove={(e) => handleChange(e)}
onMouseDown={(e) => moveOn(e)}
onMouseUp={() => moveOff()}
onMouseLeave={() => moveOff()}
>
<div className="range"
>
<span className="rounded-circle"
style={{
width:'15px',
height: '15px',
backgroundColor: 'red',
marginTop: '-6px',
left: value - 7 + 'px'
}}
></span>
<span className="rounded-circle"
style={{
width:'10px',
height: '10px',
backgroundColor: 'black',
marginTop: '-4px',
marginLeft: '195px'
}}
></span>
</div>
</div>
</div>
</Fragment>
)
}
export default Filter
CSS:
.range-container {
width: 200px;
height: 15px;
cursor: pointer;
}
.range {
width: 100%;
height: 2px;
background-color: black;
margin-bottom: 50px;
}
.range span {
width: 10px;
height: 10px;
position: absolute;
}
How can I solve this?
Upvotes: 0
Views: 421
Reputation: 1781
I couldn't rely on the events to reach what I aimed, so I had to change a little the perspective.
What I did was to store the initial position of the thumb in a variable and only change value
if it is higher than the initial value, otherwise value
receives the value of off_x
.
const off_x = 190 // initial position
let [value, setValue] = React.useState(off_x)
let [move, setMove] = React.useState(false)
const handleChange = e => {
var x = e.clientX;
if (move === true) {
value > off_x ? setValue(x) : setValue(off_x)
}
}
Now it works properly.
Upvotes: 1
Reputation: 2159
It seems like I have more success when using mouseout instead of mouseleave. The mouseleave function never get called then the click is held. You can check this code here : https://codesandbox.io/s/inspiring-edison-63s0h?file=/src/App.js
Upvotes: 1