Reputation: 581
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
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.
Like 1, after clicking the volumeBtn
you cannot drag the volumeBtn
left or right once it goes outside the `volumeRange' div.
There is a flicker from the zeroth position to the desired position.
What I Want to happen
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
.
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
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