Skip to content

Instantly share code, notes, and snippets.

@scoopandrun
Last active May 6, 2020 08:44
Show Gist options
  • Save scoopandrun/c9f16beed8506a44edcb328e0d42e3b4 to your computer and use it in GitHub Desktop.
Save scoopandrun/c9f16beed8506a44edcb328e0d42e3b4 to your computer and use it in GitHub Desktop.
Applies numeric contraints (sign and decimals) and validation on HTML input elements
/**
* Formats numeric inputs.
*
* The function allows to limit the input to figures
* that may be preceded by a plus or minus sign
* and a decimal separator.
*
* Applies to "input" elements with the attribute data-decimal=*
* where * is (+/-)(\d).
*
* HTML: <input data-decimal=*>
*
* Examples :
*
* - data-decimal="2" (+) or (-) number with 2 decimals
* - data-decimal="-1" Negative number with 1 decimal
* - data-decimal="+0" Positive integer (0 decimal)
* - data-decimal="+" Positive number (free decimals)
* - data-decimal="-" Negative number (free decimals)
* - data-decimal="" (+) or (-) number (free decimals)
*
* @author Nicolas DENIS
* @link https://gist.github.com/ScoopAndrun/c9f16beed8506a44edcb328e0d42e3b4
* @license Unlicense https://spdx.org/licenses/Unlicense.html
*
* @global
*
* @listens event:keypress
* @listens event:blur
*/
(function () {
"use strict";
// Name of the "data" attribute chosen for the numeric inputs
const data_attribute = "data-decimal";
// Get the decimal separator used by the browser
const separator = parseFloat(1.1).toFixed(1).substring(1, 2);
const numeric_inputs = document.querySelectorAll("[" + data_attribute + "]");
for (let i = numeric_inputs.length; i--; ) {
const input = numeric_inputs[i];
const data_attr_value = input.getAttribute(data_attribute);
// Converting the input element to type "text"
input.setAttribute("type", "text");
let sign = "";
let point = "";
let decimals = "";
// Checking sign and amount of decimals
if (data_attr_value === "") {
sign = "[\\+-]?";
} else if (data_attr_value.substring(0, 1) === "-") {
sign = "-";
decimals = data_attr_value.substring(1);
} else if (data_attr_value.substring(0, 1) === "+") {
sign = "\\+?";
decimals = data_attr_value.substring(1);
} else {
sign = "[\\+-]?";
decimals = data_attr_value;
}
// Decimal separator for the regex
if (decimals !== "0" || decimals === "") {
point = "([,.]?)";
}
// RegExp applied to the input element
const regex = new RegExp(
"^" + sign + "[0-9]*" + point + "[0-9]{0," + decimals + "}$"
);
/* User input check */
input.addEventListener("keypress", function (event) {
// Preventing default keystroke
event.preventDefault();
// Saving current cursor/selection position
let caret_start = this.selectionStart;
let caret_end = this.selectionEnd;
// If period/coma is input, replacement by browser decimal separator
let key =
event.which === 44 || event.which === 46 ? separator : event.key;
// Target value to be checked
let target_value =
this.value.substring(0, caret_start) +
key +
this.value.substring(caret_end);
// Keystroke validation
if (regex.test(target_value)) {
this.value = target_value;
this.setSelectionRange(caret_start + 1, caret_start + 1); // Replaces the cursor at the right position
}
});
/* Decimals after the separator */
input.addEventListener("blur", function () {
if (this.value !== "") {
// Check for decimal separators (in case of pasted value)
this.value = this.value.replace(/,|\./, separator);
// Remove spaces
this.value = this.value.replace(/\s/g, "");
if (
data_attr_value === "" ||
data_attr_value === "+" ||
data_attr_value === "-"
) {
// For inputs with no amount of decimals chosen
this.value = parseFloat(this.value);
} else {
// For inputs with a specific amount of decimals chosen
this.value = parseFloat(this.value).toFixed(decimals);
}
}
});
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment