Reputation: 323
I have a range slider with an output element above it. Below the output element is a down arrow that follows wherever the thumb is on the slider.
I'm using CSS to place the arrow and to animate it as well.
Unfortunately, the arrow is not consistently above the thumb, considering that the slider is responsive.
Thanks for your help!
$(function() {
var slider = document.getElementById("range-slider");
var output = document.getElementById("range-slider-output");
output.innerHTML = slider.value;
slider.oninput = function() {
output.innerHTML = this.value;
// resets state of user icon
$('.fa-user').removeClass("disabled");
if (output.innerHTML == 1) {
document.getElementById("range-slider").setAttribute("aria-valuenow", "1");
$('.range-slider-icons li:nth-of-type(2) span, .range-slider-icons li:nth-of-type(3) span, .range-slider-icons li:nth-of-type(4) span').addClass('disabled');
} else if (output.innerHTML == 2) {
document.getElementById("range-slider").setAttribute("aria-valuenow", "2");
$('.range-slider-icons li:nth-of-type(3) span, .range-slider-icons li:nth-of-type(4) span').addClass('disabled');
} else if (output.innerHTML == 3) {
document.getElementById("range-slider").setAttribute("aria-valuenow", "3");
$('.range-slider-icons li:nth-of-type(4) span').addClass('disabled');
} else {
document.getElementById("range-slider").setAttribute("aria-valuenow", "4");
}
}
});
.range-slider-output {
position: relative;
display: inline-block;
padding: 0.2em 0.75em 0.25em;
color: #fff;
text-align: center;
background: #666;
border-radius: 3px;
width: 100%;
left: calc(50%);
flex: 0 0 5%;
align-self: center;
margin: 0;
font-size: 28px;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
top: -92px;
}
.range-slider-output::before {
position: absolute;
top: 50%;
left: 0;
content: "";
}
.range-slider-lg .range-slider-output::before {
top: 48px;
width: 0;
height: 0;
border-style: solid;
border-width: 12px 12px 0 12px;
border-color: #666 transparent transparent transparent;
transition: all 0.7s ease-out;
}
.range-slider-wrap {
min-width: 250px;
}
input[aria-valuenow='1']+.range-slider-output::before {
left: 1rem;
}
input[aria-valuenow='2']+.range-slider-output::before {
left: 32.5%;
}
input[aria-valuenow='3']+.range-slider-output::before {
left: 64%;
}
input[aria-valuenow='4']+.range-slider-output::before {
left: 94%;
}
input[type='range'] {
width: 100%;
cursor: pointer;
padding-top: 90px;
}
input[type='range'] {
-webkit-appearance: none;
}
input[type='range']::-webkit-slider-runnable-track {
width: 100%;
height: 5px;
background: #e6e5e5;
border: 1px solid #999;
border-radius: 3px;
-webkit-appearance: none;
padding: 0 0.5rem;
}
input[type='range']::-moz-range-track {
width: 100%;
height: 5px;
background: #e6e5e5;
border: 1px solid #999;
border-radius: 3px;
}
input[type=range]::-webkit-slider-thumb {
width: 28px;
height: 28px;
margin-top: -11px;
background: #999;
border: 1px solid #666;
border-radius: 50%;
-webkit-appearance: none;
}
input[type='range']::-moz-range-thumb {
width: 28px;
height: 28px;
margin-top: -11px;
background: #999;
border: 1px solid #666;
border-radius: 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<div class="range-slider-wrap range-slider-lg">
<input class="range-slider ng-valid ng-dirty ng-touched" step="1" type="range" id="range-slider" min="1" max="4" data-tooltip-top="true" aria-valuenow="3" aria-valuemin="1" aria-valuemax="4">
<output class="range-slider-output num" id="range-slider-output" for="range-slider"></output>
</div>
</div>
</div>
</div>
</div>
Upvotes: 0
Views: 1524
Reputation: 33943
The thing is that notches are not simply positionned by percentage like:
The slider-thumb
has a dimension... 28px in your case.
And there are spacings before the first notch and the last...
The arrow also has a width!
So the alignment calculation can become twisty...
I didn't find anything close to a all cases formula. But using calc()
with a "logical percentage" and an offset in px
, it does the job right and is responsive. It just a matter of a couple try and fail to find the right offset.
Here is the only lines I changed from your snippet:
input[aria-valuenow='1'] + .range-slider-output::before {
left: 11px;
}
input[aria-valuenow='2'] + .range-slider-output::before {
left: calc(33.3% - 4px);
}
input[aria-valuenow='3'] + .range-slider-output::before {
left: calc(66.6% - 20px);
}
input[aria-valuenow='4'] + .range-slider-output::before {
left: calc(100% - 36px);
}
I also added bootstrap files (that were in your fiddle).
Updated Fiddle
Working snippet
$(function() {
var slider = document.getElementById("range-slider");
var output = document.getElementById("range-slider-output");
output.innerHTML = slider.value;
slider.oninput = function() {
output.innerHTML = this.value;
// resets state of user icon
$('.fa-user').removeClass("disabled");
if (output.innerHTML == 1) {
document.getElementById("range-slider").setAttribute("aria-valuenow", "1");
$('.range-slider-icons li:nth-of-type(2) span, .range-slider-icons li:nth-of-type(3) span, .range-slider-icons li:nth-of-type(4) span').addClass('disabled');
} else if (output.innerHTML == 2) {
document.getElementById("range-slider").setAttribute("aria-valuenow", "2");
$('.range-slider-icons li:nth-of-type(3) span, .range-slider-icons li:nth-of-type(4) span').addClass('disabled');
} else if (output.innerHTML == 3) {
document.getElementById("range-slider").setAttribute("aria-valuenow", "3");
$('.range-slider-icons li:nth-of-type(4) span').addClass('disabled');
} else {
document.getElementById("range-slider").setAttribute("aria-valuenow", "4");
}
}
});
.range-slider-output {
position: relative;
display: inline-block;
padding: 0.2em 0.75em 0.25em;
color: #fff;
text-align: center;
background: #666;
border-radius: 3px;
width: 100%;
left: calc(50%);
flex: 0 0 5%;
align-self: center;
margin: 0;
font-size: 28px;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
top: -92px;
}
.range-slider-output::before {
position: absolute;
top: 50%;
left: 0;
content: "";
}
.range-slider-lg .range-slider-output::before {
top: 48px;
width: 0;
height: 0;
border-style: solid;
border-width: 12px 12px 0 12px;
border-color: #666 transparent transparent transparent;
transition: all 0.7s ease-out;
}
.range-slider-wrap {
min-width: 250px;
}
input[aria-valuenow='1'] + .range-slider-output::before {
left: 11px;
}
input[aria-valuenow='2'] + .range-slider-output::before {
left: calc(33.3% - 4px);
}
input[aria-valuenow='3'] + .range-slider-output::before {
left: calc(66.6% - 20px);
}
input[aria-valuenow='4'] + .range-slider-output::before {
left: calc(100% - 36px);
}
input[type='range'] {
width: 100%;
cursor: pointer;
padding-top: 90px;
}
input[type='range'] {
-webkit-appearance: none;
}
input[type='range']::-webkit-slider-runnable-track {
width: 100%;
height: 5px;
background: #e6e5e5;
border: 1px solid #999;
border-radius: 3px;
-webkit-appearance: none;
padding: 0 0.5rem;
}
input[type='range']::-moz-range-track {
width: 100%;
height: 5px;
background: #e6e5e5;
border: 1px solid #999;
border-radius: 3px;
}
input[type=range]::-webkit-slider-thumb {
width: 28px;
height: 28px;
margin-top: -11px;
background: #999;
border: 1px solid #666;
border-radius: 50%;
-webkit-appearance: none;
}
input[type='range']::-moz-range-thumb {
width: 28px;
height: 28px;
margin-top: -11px;
background: #999;
border: 1px solid #666;
border-radius: 50%;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<div class="range-slider-wrap range-slider-lg">
<input class="range-slider ng-valid ng-dirty ng-touched" step="1" type="range" id="range-slider" min="1" max="4" data-tooltip-top="true" aria-valuenow="3" aria-valuemin="1" aria-valuemax="4">
<output class="range-slider-output num" id="range-slider-output" for="range-slider"></output>
</div>
</div>
</div>
</div>
</div>
Upvotes: 1