Clamp a Number Within a Range
Clamping a value into `[min, max]` shows up in scroll math, slider components, RGB arithmetic, and clamping pagination cursors. The one-liner is trivial, but the helpful version handles swapped bounds, `NaN`, and integer-only callers. This snippet covers the simple form, an `inclusive`/`exclusive` mapping flag, and an integer variant that snaps to whole numbers in the range.
201 views
4
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
console.log(clamp(5, 0, 10)); // 5
console.log(clamp(-3, 0, 10)); // 0
console.log(clamp(99, 0, 10)); // 10Nesting Math.min and Math.max is the shortest clamp: max(value, min) lifts values below the floor, then min(...) against the ceiling caps anything above. The function is total over finite numbers and returns the value unchanged when it is already inside the range. NaN short-circuits both Math.min and Math.max to NaN, which is usually what you want (callers can detect and reject). One gotcha: if min > max, the function still runs but returns min, which silently masks a caller bug; the next accordion guards against that.
function clampSafe(value, a, b) {
if (Number.isNaN(value)) return NaN;
const min = Math.min(a, b);
const max = Math.max(a, b);
return Math.min(Math.max(value, min), max);
}
console.log(clampSafe(5, 10, 0)); // bounds swapped, still 5
console.log(clampSafe(NaN, 0, 10)); // NaN
console.log(clampSafe(99, -Infinity, Infinity)); // 99 unchangedSwapped bounds (min accidentally larger than max) produce silent wrong answers in the naive form because min(max(v, 10), 0) collapses to 0. Computing Math.min(a, b) and Math.max(a, b) first normalises the order so callers cannot misuse the helper. Adding an explicit NaN short-circuit is optional but documents intent clearly. Infinity bounds work as expected because Math.min/Math.max treat them transparently, which is handy for half-open ranges (clamp from 0 to Infinity).
function clampInt(value, min, max, mode = 'round') {
const lo = Math.min(min, max);
const hi = Math.max(min, max);
const snap = mode === 'floor' ? Math.floor : mode === 'ceil' ? Math.ceil : Math.round;
const clamped = Math.min(Math.max(value, lo), hi);
return snap(clamped);
}
console.log(clampInt(2.4, 0, 10)); // 2
console.log(clampInt(2.6, 0, 10)); // 3
console.log(clampInt(2.4, 0, 10, 'ceil')); // 3
console.log(clampInt(15, 0, 10)); // 10Many UI features (slider step, pagination index, pixel snapping) need an integer in the range, so rounding after clamp is the standard recipe. Switching the rounding mode lets callers pick round (default), floor (truncate toward zero for positives), or ceil (snap up), which covers nearly every real use case. Doing the clamp before the round avoids drifting just past the boundary on 0.5 rounding behaviour around the bounds. For half-even or banker's rounding, swap in your own snapper since Math.round always rounds toward positive infinity on .5.
