I want to create an input React component for a monetary amount and I would like the placeholder to be 0.00 but upon typing, the value should be updated digit by digit without the need to type a .
. So, for example, an amount of 1,234.56 should show as:
0.01
0.12
1.23
12.34
123.45
1,234.56
And when erasing a number then the revert order:
1,234.56
123.45
12.34
1.23
0.12
0.01
But, unfortunately, I can't get any of it to work. I tried an onChange
event and onKeyDown
event too with no success.
I am using a ShadCN component if that matters
You could format the currency with a number formatter, and parse it with parseInt
.
You will need to format on input
, and validate on keydown
.
const currencyFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2
});
initCurrencyFields();
document.forms['receipt'].addEventListener('submit', onSubmit);
function onSubmit(e) {
e.preventDefault();
const values = [...e.target.elements]
.filter(el => el.classList.contains('currency-input'))
.map(el => el.value || '?');
console.log(...values);
}
function initCurrencyFields(selector = '.currency-input') {
document.querySelectorAll(selector)
.forEach(el => {
el.placeholder = '$0.00';
el.addEventListener('input', formatCurrency);
el.addEventListener('keydown', validateInput);
});
}
function formatCurrency(e) {
const inputField = e.target;
const number = parseInt(inputField.value.replace(/[^0-9]/g, ''), 10);
inputField.value = !isNaN(number)
? currencyFormatter.format(number / 100)
: ''; // Clear field if input is not a valid number
}
function validateInput(e) {
// Allow only number keys, control keys, and decimal points
if (!/[0-9]|Backspace|ArrowLeft|ArrowRight|Delete|Tab|Enter/.test(e.key)) {
e.preventDefault(); // Prevent non-numeric and non-control keys
}
}
.as-console-wrapper { max-height: 4.33rem !important; }
*, *::before, *::after {
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 1rem;
}
form[name="receipt"] {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.5rem;
}
form[name="receipt"] > button[type="submit"] {
grid-column: 1 / 3;
}
<form name="receipt">
<label for="item-a">Item A:</label>
<input class="currency-input" id="item-a" name="a" type="text">
<label for="item-b">Item B:</label>
<input class="currency-input" id="item-b" name="b" type="text">
<label for="item-c">Item C:</label>
<input class="currency-input" id="item-c" name="c" type="text">
<button type="submit">Submit</button>
</form>