Issue
I'm building a range/slider to compare a users results to an average. I'm really struggling to get the text under the slider to behave as I want:
I have managed to get the arrow, percentage, and text to be centered correctly as in the first two images, but when I set the position too high or low, the text overflows into another area of the page. What I want is behaviour like the last two images, where the text stays within its bounds.
I've tried two different ways with the same end result, and now I'm stuck:
<div style="`margin-left:${areaScore}%;margin-right:${100 - areaScore}%;text-align:center`">
<div style="transform: translateX(-50%)" >^</div>
<div style="transform: translateX(-50%); width: 50px">
{{ areaScore }}%
</div>
<div style="transform: translateX(-50%); width: 150px">
The average in your area
</div>
</div>
<div style="position: relative; text-align: center">
<div :style="`position:absolute;left:${areaScore}%`">^</div>
<div :style="`position:absolute;left:${areaScore}%;top:10px`">
{{ areaScore }}%
</div>
<div :style="`position:absolute;left:${areaScore}%;top:24px`">
The average in your area
</div>
</div>
I should probably mention that I'm using Vue, hence the moustaches, colon before style, and parameterisation of areaScore, but I don't think it's particularly relevant to my problem.
I'm sure this problem is solvable, but I've wasted so much time trying to find a solution, and I'm hoping someone can point me in the right direction.
Solution
This requires some calculations in JavaScript. I don't know vue so I will give my answer using pure JS. I've put together an example for moving the texts. Let's see the HTML first:
<div class="container">
<input class="slider" type="range" min="0" max="100">
<div>
<span class="percent">50%</span>
</div>
<div>
<span class="text">Some long text</span>
</div>
</div>
I put the text and the percent (like your score) in span
elements inside of div
elements. The reason is that the a div
by default stretches over the complete width (because of display: block
). Inside needs to be an element whose width we can read and whose position we can manipulate.
Now span
can't be positioned because it is displayed inline
. So, I use display: inline-block
on it:
.percent, .text {
display: inline-block;
}
The last piece is the calculation in JavaScript:
const slider = document.querySelector('.slider');
const percent = document.querySelector('.percent');
const text = document.querySelector('.text');
slider.value = 50;
setPercent(50);
positionElement(text, 50);
slider.addEventListener("input", (event) => {
setPercent(event.target.value);
positionElement(text, event.target.value);
});
function setPercent(value) {
percent.textContent = value + '%';
positionElement(percent, value);
}
function positionElement(element, value) {
const width = element.offsetWidth;
const xPosition = Math.min(Math.max(value - (width / 2), 0), 100 - width);
element.style.transform = `translateX(${xPosition}px)`;
}
The keys are using translateX
to move the span
elements like you already did. Using Math.min
and Math.max
, we can set boundaries to this movement, depending on the width of the elements. Important: first set the new text and then get the width and change the translateX
. The 100 is based on a width of 100px of the sliding area.
If we want to update the position while dragging, it is necessary to update on input
events as found in https://stackoverflow.com/a/19067260/4874075.
I've put the example on codepen: https://codepen.io/andreas-tennert/pen/YzeMoee
Answered By - Andreas
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.