Last active
June 6, 2024 18:35
-
-
Save jagoncalves14/c6ef4ff56155a093bd5978d7e05ed8e8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Number Input Mask</title> | |
</head> | |
<body> | |
<p>EN/US</p> | |
<input type="text" class="numberInput" placeholder="1,123.0000" locale="en-US"> | |
<br /> | |
<p>GB</p> | |
<input type="text" class="numberInput" placeholder="1,123.0000" locale="en-GB"> | |
<br /> | |
<p>BR FR</p> | |
<input type="text" class="numberInput" placeholder="1,123.0000" locale="br-FR"> | |
<br /> | |
<p>FR</p> | |
<input type="text" class="numberInput" placeholder="1 123,0000" locale="fr-FR" value="1123"> | |
<br /> | |
<p>PT</p> | |
<input type="text" class="numberInput" placeholder="1 123,0000" locale="pt-PT" value="1123"> | |
<br /> | |
<p>SE</p> | |
<input type="text" class="numberInput" placeholder="1 123,0000" locale="sv-SE"> | |
<br /> | |
<p>FI</p> | |
<input type="text" class="numberInput" placeholder="1 123,0000" locale="fi-FI"> | |
<br /> | |
<p>DE</p> | |
<input type="text" class="numberInput" placeholder="1.123,0000" locale="de-DE"> | |
<script> | |
function getDecimalSeparator(locale) { | |
const number = 1.1 // A number with a decimal part | |
const formattedNumber = new Intl.NumberFormat(locale).formatToParts(number) | |
// Find the decimal separator | |
const decimalSeparator = formattedNumber.find(part => part.type === 'decimal').value | |
return decimalSeparator | |
} | |
function formatInputValue(value, decimalSeparator, locale) { | |
const fractionDigits = value.split(decimalSeparator)[1]?.length || '' | |
const numberToFormat = useCurrencyLocaleToMajorUnit(value, locale) | |
if (value.endsWith(`${decimalSeparator}`)) { | |
return new Intl.NumberFormat(locale).format(numberToFormat) + `${decimalSeparator}` | |
} | |
if (fractionDigits) { | |
return new Intl.NumberFormat(locale, { | |
minimumFractionDigits: fractionDigits | |
}).format(numberToFormat) | |
} | |
return new Intl.NumberFormat(locale).format(numberToFormat) | |
} | |
function applyInputMask(inputElement) { | |
if (!inputElement.value?.length) return | |
const locale = inputElement.getAttribute('locale') | |
const decimalSeparator = getDecimalSeparator(locale) | |
const cursorPosition = inputElement.selectionStart // Store cursor position | |
let value = inputElement.value | |
// Remove non-numeric characters except dot | |
value = value.replace(new RegExp(`[^\\d${decimalSeparator}]`, 'g'), '') | |
// Allow only one dot | |
const separatorIndex = value.indexOf(`${decimalSeparator}`) | |
if (separatorIndex !== -1 && value.indexOf(`${decimalSeparator}`, separatorIndex + 1) !== -1) { | |
// If there's more than one dot, remove extras | |
value = value.slice(0, separatorIndex + 1) + value.slice(separatorIndex + 1).replace( | |
new RegExp(`\\${decimalSeparator}g`), | |
'' | |
) | |
} | |
// Allow maximum four digits after the dot | |
const parts = value.split(`${decimalSeparator}`) | |
if (parts[1] && parts[1].length > 4) { | |
parts[1] = parts[1].slice(0, 4) | |
value = parts.join(`${decimalSeparator}`) | |
} | |
// Store old value for cursor positioning | |
const oldValue = inputElement.value | |
const newInputValue = formatInputValue(value, decimalSeparator, locale) | |
inputElement.value = newInputValue | |
// Adjust cursor position after adding separators | |
const separatorAdded = newInputValue.length - oldValue.length | |
const newPosition = cursorPosition + separatorAdded | |
inputElement.setSelectionRange(newPosition, newPosition) | |
} | |
function applyFormatOnBlur(inputElement) { | |
const locale = inputElement.getAttribute('locale') | |
const decimalSeparator = getDecimalSeparator(locale) | |
let numberInputValue = inputElement.value | |
if (!numberInputValue?.length) return | |
if (numberInputValue.endsWith(`${decimalSeparator}`)) { | |
numberInputValue += 0 | |
} | |
const numberFormat = useCurrencyLocaleToMajorUnit(numberInputValue, locale) | |
inputElement.value = new Intl.NumberFormat(locale, { | |
minimumFractionDigits: 4 | |
}).format(numberFormat) | |
} | |
function useCurrencyLocaleToMajorUnit(value, locale) { | |
// Obtain information about the grouping and decimal separators for the current locale | |
const parts = new Intl.NumberFormat(locale).formatToParts(1111.1); | |
const group = parts.find((part) => part.type === 'group')?.value | |
const decimal = parts.find((part) => part.type === 'decimal')?.value | |
// Remove all occurrences of the group separator | |
let normalizedValue = value.replace(new RegExp(`\\${group}`, 'g'), ''); | |
// Replace the decimal separator with a dot | |
normalizedValue = normalizedValue.replace( | |
new RegExp(`\\${decimal}`, 'g'), | |
'.' | |
) | |
// Replace the narrow no-break space with a regular space | |
normalizedValue = normalizedValue.replace(/\u202F/g, ' '); | |
// Remove any currency symbols and non-numeric characters except for the decimal point and space | |
normalizedValue = normalizedValue.replace(/[^\d. -]/g, ''); | |
// Convert the resulting string into a number | |
const result = parseFloat(normalizedValue.replace(/\s+/g, '')) || 0; | |
return isNaN(result) ? 0 : result; | |
} | |
document.addEventListener('DOMContentLoaded', function () { | |
const numberInput = document.querySelectorAll('.numberInput') | |
numberInput.forEach((el) => { | |
if (el.value) applyInputMask(el) | |
el.addEventListener('input', (event) => applyInputMask(event.target)) | |
el.addEventListener('blur', (event) => applyFormatOnBlur(event.target)) | |
}) | |
}) | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment