Personal Information
Personal Information

Reputation: 581

Building a custom volume slider

I've found this and tried to fix it as the logic behind it is similar to what I'm trying to achieve. I've manage to get it working with minimal editing. but it isn't working as expected.

note: I have commented out the click feature as it is working fine.

What is happening

  1. If you click on the volumeBtn and accidentally move the cursor out of the volumeRange div height or width while sliding either left or right, the mouseup event listener doesn't get executed when you stop clicking the mouse.

  2. Like 1, after clicking the volumeBtn you cannot drag the volumeBtn left or right once it goes outside the `volumeRange' div.

  3. There is a flicker from the zeroth position to the desired position.

What I Want to happen

  1. If you click on the volumeBtn then stop clicking the mouse, the mouseup event should be executed even if the cursor is no longer on the volumeRange.

  2. If you click on the volumeBtn you should be able to drag the volumeBtn left or right even if the cursor is no longer on the volumeRange.

    const volume = document.querySelector('.volume');
    const volumeRange = document.querySelector('.volume-range');
    const volumeBtn = document.querySelector('.volume-button');

    // volumeRange.addEventListener("click", volumeClick    );
    //     function volumeClick(event) {
    //         let x = event.offsetX;
    //         volume.style.width = (Math.floor(x) + 10) + 'px';
    //     }



        let mouseIsDown = false;

        volumeBtn.addEventListener("mouseup", up);
        volumeBtn.addEventListener("mousedown", down);
        volumeRange.addEventListener("mousemove", volumeSlide);

        function down(){ mouseIsDown = true; }
        function up(){ mouseIsDown = false; }

        function volumeSlide(event) {
            if (mouseIsDown) {
                let x = event.offsetX;
                console.log(x);
                volume.style.width = Math.floor(x + 10) + 'px';
            }
        }
    body {
        background-color: #2a2a2a;
    }

    .volume-range {
        margin-top: 80px;
        height: 5px;
        width: 250px;
        background: #555;
        border-radius: 15px;

    }

    .volume-range>.volume {
        height: 5px;
        width: 50px;
        background: #2ecc71;
        border: none;
        border-radius: 10px;
        outline: none;
        position: relative;
    }

    .volume-range>.volume>.volume-button {
        width: 20px;
        height: 20px;
        border-radius: 20px;
        background: #FFF;
        position: absolute;
        right: 0;
        top: 50%;
        transform: translateY(-50%);
        cursor: pointer;
        outline: none;
    }
<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
    <meta charset="utf-8">
    <title>Volume</title <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
</head>
<style>

</style>

<body>
    <div class="volume-range">
        <div class="volume">
            <div class="volume-button"></div>
        </div>
    </div>
</body>

</html>

Upvotes: 0

Views: 1522

Answers (1)

Drago96
Drago96

Reputation: 1407

Why it doesn't work

This is working the way you described it because mouse events will only be fired when the mouse is inside the element that have the listeners attached.

An immediate (but not very good) solution to this will be to move the listener for "mouseup" and "mousemove" from the volumeBtn/volumeRange to the window object. This is not very good because if you will later need to remove this element, you should also remove the listeners from the window object.

Better solution

It would be better to encapsulate the slider inside another element that will give it some padding, and then put the event listeners on that "container" element. It will still stop moving when you go outside the element, but at least everything is self-contained.

This is shown in the following snippet:

const volume = document.querySelector('.volume');
    const volumeRange = document.querySelector('.volume-range');
    const volumeContainer = document.querySelector('.volume-container');
    const volumeBtn = document.querySelector('.volume-button');

    // volumeRange.addEventListener("click", volumeClick    );
    //     function volumeClick(event) {
    //         let x = event.offsetX;
    //         volume.style.width = (Math.floor(x) + 10) + 'px';
    //     }



        let mouseIsDown = false;

        volumeContainer.addEventListener("mouseup", up);
        volumeBtn.addEventListener("mousedown", down);
        volumeContainer.addEventListener("mousemove", volumeSlide, true);

        function down(){ mouseIsDown = true; }
        function up(){ mouseIsDown = false; }
				
    		const volumeRangeWidth = volumeRange.getBoundingClientRect().width; // This will be the volume limit (100%)
        
        function volumeSlide(event) {
            if (mouseIsDown) {
                let x = event.offsetX;
                if (event.target.className == "volume-container") {
                	x = Math.floor(x);
                  if (x < 0) x = 0; // check if it's too low
                  if (x > volumeRangeWidth) x = volumeRangeWidth; // check if it's too high
                  volume.style.width = (x+10) + 'px';
                }
                	
            }
        }
body {
        background-color: #2a2a2a;
    }
    
    .volume-container {
      padding: 40px 0px;
      margin: 0px 20px;
    }
    
    .volume-range {
        height: 5px;
        width: 250px;
        background: #555;
        border-radius: 15px;

    }

    .volume-range>.volume {
        height: 5px;
        width: 50px;
        background: #2ecc71;
        border: none;
        border-radius: 10px;
        outline: none;
        position: relative;
    }

    .volume-range>.volume>.volume-button {
        width: 20px;
        height: 20px;
        border-radius: 20px;
        background: #FFF;
        position: absolute;
        right: 0;
        top: 50%;
        transform: translateY(-50%);
        cursor: pointer;
        outline: none;
    }
<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
    <meta charset="utf-8">
    <title>Volume</title>
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
</head>
<style>

</style>

<body>
    <div class="volume-container">
      <div class="volume-range">
          <div class="volume">
              <div class="volume-button"></div>
          </div>
      </div>
    </div>
</body>

</html>

Other problems

In the fiddle it is also shown how to avoid the volume to go outside the container.

Upvotes: 1

Related Questions