I’m building a text tracking (letter-spacing) demo and I have range sliders updating CSS props but I’d like to extend it so that the labels also update dynamically. It’s sort of working but hacky. Is there a way to do it elegantly, so I’m not repeating output2.textContent = this.value; output2.textContent = this.value;
etc.?
Also, the UI janks when the values go from (eg) 0.09 -> 0.1 -> 0.11. Can I have it always output two decimals? Script is before the close.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Trax…text tracking tool</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Chivo&family=Chivo+Mono&family=Playfair+Display+SC:wght@700&family=Playfair+Display:wght@700&display=swap" rel="stylesheet">
<meta name="description" content="description">
<style>
* {
outline: cornflowerblue dotted 0.5px;
}
* {
margin: 0;
}
:root {
--_trax-fontFamily-caps: 'Playfair Display', serif;
--_trax-fontFamily-scaps: 'Playfair Display SC', serif;
--_trax-fontFamily-body: 'Chivo', sans-serif;
--_trax-fontFamily-mono: 'Chivo Mono', monospace;
--_trax-textTracking-caps: var(--trax-textTracking-caps, 0.00cap);
--_trax-textTracking-scaps: var(--trax-textTracking-scaps, 0.00ex);
--_trax-textTracking-body: var(--trax-textTracking-body, 0.00em);
--_trax-textTracking-mono: var(--trax-textTracking-mono, 0.00ch);
--_trax-text-measure: var(--trax-text-measure, 66em);
}
body {
box-sizing: content-box;
margin-inline: auto;
text-align: center;
max-inline-size: var(--_trax-text-measure);
padding-inline-start: 1rem;
padding-inline-end: 1rem;
display: flex;
flex-direction: column;
align-items: center;
font-family: var(--_trax-fontFamily-mono);
}
h1 {
font-size: 5rem;
}
h1.caps {
font-family: var(--_trax-fontFamily-caps);
letter-spacing: var(--_trax-textTracking-caps);
text-transform: uppercase;
}
h1.scaps {
font-family: var(--_trax-fontFamily-scaps);
letter-spacing: var(--_trax-textTracking-scaps);
}
.align-self:flex-start,
.asfs {
align-self: flex-start;
}
/* add range slider code */
</style>
</head>
<body>
<p class="align-self:flex-start">Capitals</p>
<h1 class="caps">Hamburgevons</h1>
<div class="controls">
<label for="trax-textTracking-caps" id="value-caps">0.00</label><span> cap</span>
<input name="trax-textTracking-caps" type="range" min="-0.30" max="0.30" value="0.00" step="0.01" data-uom="cap">
<button>CSS</button>
</div>
<p class="align-self:flex-start">Small Capitals</p>
<h1 class="scaps">Hamburgevons</h1>
<div class="controls">
<label for="trax-textTracking-scaps" id="value-scaps">0.00</label><span> ex</span>
<input name="trax-textTracking-scaps" type="range" min="-0.30" max="0.30" value="0.00" step="0.01" data-uom="ex">
<button>CSS</button>
</div>
<script>
const rangeSliders = document.querySelectorAll('input');
//const labels = document.querySelectorAll('label');
const output1 = document.querySelector('#value-caps');
const output2 = document.querySelector('#value-scaps');
function updateProp(event) {
const uom = this.dataset.uom || '';
document.documentElement.style.setProperty(`--_${this.name}`, this.value + uom);
output1.textContent = this.value;
output2.textContent = this.value;
}
rangeSliders.forEach(input => {
input.addEventListener('input', updateProp);
});
</script>
</body>
</html>
4
Answers
To update only the relevant
<label>
element of the<input>
element being updated, you could use DOM tree querying from the<input>
element (thethis
orevent.target
inupdateProp()
). For example, both labels are 2nd-previous element siblings, so you could reference them "relatively" with:To avoid "UI jank" when going between 0.09 ↔ 0.1 ↔ 0.11 et al, consider using the
.padEnd()
method on strings to keep the decimal places, like:I don’t think the repetition in this case is inelegant. It would likely be best to leave it as is for readability instead of going the route of trying to put together something clever that will essentially do the same thing.
As for the 2 decimals, I believe this would be the easiest way if you don’t mind the textContent being a number:
Edit:
In response to the comment below, this may be an alternate route to take since more sliders/labels will be added:
This will only update the appropriate label based on the index of the current slider, and will fix the float length for you.
For the elegancy part all you can do is put it in one line like
output1 = output2 = this.value
which works. For the decimal part I used a few if statements to convert it to decimals.One approach is as follows, with explanatory comments in the code:
JS Fiddle demo.
References:
CSS:
align-items
.align-self
.bottom
.box-sizing
.content
.display
.flex-direction
.font-family
.font-size
.inline-size
.inset
.left
.letter-spacing
.margin
.margin-inline
.max-inline-size
.opacity
.outline
.padding
.padding-inline-end
.padding-inline-start
.position
.right
.text-align
.text-transform
.top
.transition
.HTML:
<h1>
.<h2>
.<input>
.<label>
.<span>
.JavaScript:
Array.prototype.forEach()
.CSSStyleDeclaration.setProperty()
.document
.document.querySelector()
.document.querySelectorAll()
.Element.classList
API.Element.closest()
.Element.matches()
.Element.nextElementSibling()
.Event
.Event
constructor.EventTarget.addEventListener()
.EventTarget.dispatchEvent()
.HTMLElement.dataset
.Math.abs()
.Math.round()
.Math.pow()
.Node.textContent
.parseFloat()
.Number.prototype.toFixed()
.Bibliography:
<h1>
&endash;<h6>
: The HTML Section Heading Elements."