0

I was trying to build a custom vertical slider for my website instead of using the <input> tag, but when I tested it out, the slider kept going to the bottom. What's wrong with offsetY that makes it behave like this?

Screen Recording: https://i.imgur.com/Pp94E6c.gif

Code (includes HTML, CSS and JS):

<style>
    #volume {
        height: 100%;
        width: 6px;
        background: rgba(0, 0, 0, 0.25);
        border-radius: 10px;
        position: relative;
        transform: rotate(180deg);
    }

    #volume-body {
        background: rgba(0, 124, 190, 0.9);
        height: calc(50% + 6px);
        border-radius: 10px;
    }

    #volume-circle {
        position: absolute;
        top: 50%;
        left: -3px;
        background: rgba(0, 124, 190, 1);
        height: 12px;
        width: 12px;
        border-radius: 50%;
    }
</style>

<section class="volume-container" id='volume-container'>
    <div id='volume'>
        <div id="volume-body"></div>
        <div id="volume-circle"></div>
    </div>
</section>

<script>
    function volume_scrub_start(event) {
        event = event || window.event
        event.preventDefault()
        var percentage = (event.offsetY / document.getElementById('volume').offsetHeight)
        document.getElementById('volume-body').style.height = `calc(${Math.round(percentage*100)}% + 6px)`
        document.getElementById('volume-circle').style.top = `calc(${Math.round(percentage*100)}% - 6px)`
        document.getElementById('volume').onmousemove = (e) => {volume_drag(e)}
        document.getElementById('volume-circle').onmouseup = (e) => {close_volume_drag(e)}
    }

    function volume_drag(e) {
        e = e || window.event
        e.preventDefault()
        var percentage = (e.offsetY / document.getElementById('volume').offsetHeight)
        document.getElementById('volume-body').style.height = `calc(${percentage*100}% + 6px)`
        document.getElementById('volume-circle').style.top = `calc(${percentage*100}% - 6px)`
    }

    function close_volume_drag(e) {
        document.getElementById('volume').onmousemove = null
        document.getElementById('volume-circle').onmouseup = null
    }

    document.getElementById('volume-circle').onmousedown = (event) => {volume_scrub_start(event)}
</script>
  • The behavior appears to be from `onmousemove` in `volume_scrub_start(event)`. Notice the change of behavior if you use `onmousedown` – Tim R Jul 21 '23 at 05:31

1 Answers1

0

I took out the 180deg rotation of the slider because I couldn't work out the maths but this might help get you started.

From this answer I used the mousedown event on the element to start allowing the slider movement but then document.onmouseup and document.onmousemove to allow the slider to still work when the mouse is outside of the slider when still dragging.

With the 180 deg transform it will use the opposite value on the slider.

Hope this is somewhat helpful:

<style>
    #volume {
        height: 100%;
        width: 6px;
        background: rgba(0, 0, 0, 0.25);
        border-radius: 10px;
        position: relative;
        /* transform: rotate(180deg); */
    }

    #volume-body {
        background: rgba(0, 124, 190, 0.9);
        height: calc(50% + 6px);
        border-radius: 10px;
    }

    #volume-circle {
        position: absolute;
        top: 50%;
        left: -3px;
        background: rgba(0, 124, 190, 1);
        height: 12px;
        width: 12px;
        border-radius: 50%;
    }
</style>

<section class="volume-container" id="volume-container">
    <div id="volume">
        <div id="volume-body"></div>
        <div id="volume-circle"></div>
    </div>
</section>

<script>
    var sliderMouseDown = false;

    function volume_scrub_start(event) {
        event = event || window.event;
        event.preventDefault();
        var percentage =
            event.offsetY / document.getElementById("volume").offsetHeight;
        document.getElementById(
            "volume-body"
        ).style.height = `calc(${Math.round(percentage * 100)}% + 6px)`;
        document.getElementById("volume-circle").style.top = `calc(${Math.round(
            percentage * 100
        )}% - 6px)`;
        document.getElementById("volume").onmousemove = (e) => {
            volume_drag(e);
        };
        document.getElementById("volume-circle").onmouseup = (e) => {
            close_volume_drag(e);
        };
    }

    function volume_drag(e) {
        e = e || window.event;
        e.preventDefault();
        var percentage =
            e.offsetY / document.getElementById("volume").offsetHeight;
        document.getElementById("volume-body").style.height = `calc(${
            percentage * 100
        }% + 6px)`;
        document.getElementById("volume-circle").style.top = `calc(${
            percentage * 100
        }% - 6px)`;
    }

    function close_volume_drag(e) {
        document.getElementById("volume").onmousemove = null;
        document.getElementById("volume-circle").onmouseup = null;
    }

    document.getElementById("volume-circle").onmousedown = (event) => {
        sliderMouseDown = true;
    };
    document.onmouseup = (event) => {
        sliderMouseDown = false;
        close_volume_drag(event)
    };
    document.onmousemove = (event) => {
        if (sliderMouseDown) {
            volume_scrub_start(event);
        }
    };
</script>
p7dxb
  • 397
  • 8