Berg_Durden
Berg_Durden

Reputation: 1781

How to limit the range of an element?

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:

enter image description here

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

Answers (2)

Berg_Durden
Berg_Durden

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

Martin J
Martin J

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

Related Questions