Range Slider and Progress Bar
const NUM = 8;
const NUM_M_1 = NUM - 1;
const ui = document.createElement('div')
ui.innerHTML = `
<div class="ui">
<div class="progress" data-ref>
<div class="progressFill" data-ref></div>
</div>
<div class="text" data-ref>
0% of 100%
</div>
<div class="dots" data-ref>
${
`<div></div>`.repeat(NUM)
}
</div>
<label>
drag the slider...
<input class="slider" type="range" min="0" max="100" step="1" value="0" data-ref/>
</label>
</div>
<style>
body {
font-family: sans-serif;
}
.progress, .dots {
position: relative;
width: 80%;
margin: 0 auto;
height: 30px;
border: 1px solid black;
}
.progressFill {
height: 100%;
width: 0;
background: red;
}
.text {
padding: 1em;
background: #ccc;
margin: .5em;
font-weight: bold;
}
label {
display: block;
margin: 1em;
}
.slider {
cursor: pointer;
}
.dots {
border: none;
}
.dots div {
height: 100%;
width: ${100 / NUM}%;
float: left;
transition: background 400ms ease-out;
background: transparent;
border-radius: 500px;
box-shadow: inset 0 0px 0 3px blue;
}
.dots div:nth-child(1) {
background: red;
}
</style>
`;
document.body.appendChild(ui);
// select everything with a `data-ref`
const els = {};
;[...document.querySelectorAll('[data-ref]')]
.forEach(el => {
els[el.classList[0]] = el;
});
function update(e) {
// normal prog bar
const val = e.target.value;
els.progressFill.style.width = `${val}%`;
els.text.innerHTML = `
${val}% of 100%
`;
// segmented dot prog bar
const idx = Math.floor(val / (100 / NUM));
if (idx < NUM) {
for (let i = 0; i < NUM; i++) {
els.dots.children[i]
.style.background = i <= idx ? 'red' : 'white'
}
}
}
els.slider.addEventListener('input', update);
els.slider.addEventListener('change', update);
Drag slider and watch two progress bars and a text readout update. There are some interesting vanilla tricks in this one.
This trick and a variety of variations on it is pretty powerful:
const els = {};
[...document.querySelectorAll('[data-ref]')].forEach(el => {
els[el.classList[0]] = el;
});