86

I seem unable to work out what to use for accepting monetary values on a form.

I have tried...

<input type="number" min="0" max="10000" step="1" name="Broker_Fees" id="broker_fees" required="required">

But that then won't allow for pence entries.

I want the incremental button control to go up in pounds, but still want the ability to enter pence.

Who would want to use an incremental button that moved 1p at a time?

Perhaps I'm using the wrong control , but I can't find a money/currency control?

Can someone please advise the best way to accept monetary values (including commas, decimal places and currency symbol) using HTML5?

Thanks, 1DMF

Rubén
  • 24,097
  • 9
  • 55
  • 116
1DMF
  • 1,625
  • 3
  • 13
  • 16
  • You have to specify the `step` attribute with decimal places. See: [Is there a float input type in HTML(5)?](http://stackoverflow.com/questions/19011861/is-there-a-float-input-type-in-html5) – feeela Jun 11 '14 at 13:18
  • 1
    Please read the question again, and be sure you understand I had already done this and it produces undesired results... how can this be a duplication of the other thread under these circumstances. – 1DMF Jun 11 '14 at 13:43
  • Well, you are asking for "the best way to accept monetary values". Accepting an input is not the same as providing alternative input methods. Using a number input, the user can still directly insert a valid number that matches the given attributes. You can still add additional buttons, that increment by pounds… – feeela Jun 11 '14 at 13:45
  • Unfortunatley you can't control this with HTML5. But maybe this might be interesting for you: http://jsfiddle.net/trixta/UC6tG/embedded/result,html,js,css/ – alexander farkas Jun 11 '14 at 15:27
  • Thanks farkas, but I can't seem to make it work, the spin buttons don't do anything and I can't type in the input either. Seems to work in FF but not IE – 1DMF Jun 11 '14 at 15:46
  • Keep in mind that at least Chrome forbids entering commas, if your format uses them instead of dots, like `25,50` instead of `25.50` – Artur Beljajev May 02 '19 at 15:37

8 Answers8

74

Enabling Fractions/Cents/Decimals for Number Input

In order to allow fractions (cents) on an HTML5 number input, you need to specify the "step" attribute to = "any":

<input type="number" min="1" step="any" />

This will specifically keep Chrome from displaying an error when a decimal/fractional currency is entered into the input. Mozilla, IE, etc... don't error out if you forget to specify step="any". W3C spec states that step="any" should, indeed, be needed to allow for decimals. So, you should definitely use it. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number#step

Note that if you want the up/down buttons to do a specific granularity, then you must specify a numeric step such as ".01".

Also, the number input is now pretty widely supported (>90% of users).


What Input Options are there for Money/Currency?

The title of the question has since changed and takes on a slightly different meaning. One could use both number or text input in order to accept money/decimals.

For an input field for currency/money, it is recommended to use input type of number and specify appropriate attributes as outlined above. As of 2020, there is not a W3C spec for an actual input type of currency or money.

Main reason being it automatically coerces the users into entering a valid standard currency format and disallows any alphanumeric text. With that said, you could certainly use the regular text input and do some post processing to only grab the numeric/decimal value (there should be server side validation on this at some point as well).

The OP detailed a requirement of currency symbols and commas. If you want fancier logic/formatting like that, (as of 2020) you'll need to create custom JS logic for a text input or find a plugin.

karns
  • 4,469
  • 5
  • 24
  • 45
58

Try using step="0.01", then it will step by a penny each time.

eg:

<input type="number" min="0.00" max="10000.00" step="0.01" />
vsync
  • 87,559
  • 45
  • 247
  • 317
Travis Reeder
  • 31,147
  • 12
  • 77
  • 80
  • 4
    This works well. But worth noting it cycles through numbers like "1.28... 1.29... **1.3...** 1.31...". I.e. trailing zeros are absent (because it's a number not currency). Related: [How can I make the HTML5 number field display trailing zeroes?](https://stackoverflow.com/questions/7790561/how-can-i-make-the-html5-number-field-display-trailing-zeroes) – Duncan Jones Apr 30 '19 at 06:29
  • and remember not all languages use decimal for their monetary system. – Amirreza Dec 13 '19 at 02:10
33

Using javascript's Number.prototype.toLocaleString:

var currencyInput = document.querySelector('input[type="currency"]')
var currency = 'GBP' // https://www.currency-iso.org/dam/downloads/lists/list_one.xml

 // format inital value
onBlur({target:currencyInput})

// bind event listeners
currencyInput.addEventListener('focus', onFocus)
currencyInput.addEventListener('blur', onBlur)


function localStringToNumber( s ){
  return Number(String(s).replace(/[^0-9.-]+/g,""))
}

function onFocus(e){
  var value = e.target.value;
  e.target.value = value ? localStringToNumber(value) : ''
}

function onBlur(e){
  var value = e.target.value

  var options = {
      maximumFractionDigits : 2,
      currency              : currency,
      style                 : "currency",
      currencyDisplay       : "symbol"
  }
  
  e.target.value = (value || value === 0) 
    ? localStringToNumber(value).toLocaleString(undefined, options)
    : ''
}
input{
  padding: 10px;
  font: 20px Arial;
  width: 70%;
}
<input type='currency' value="123" placeholder='Type a number & click outside' />

Here's a very simple demo illustrating the above method (HTML-only)


I've made a tiny React component if anyone's interested

vsync
  • 87,559
  • 45
  • 247
  • 317
  • This is a tidy solution, but beware the localStringToNumber conversion will fail for users in some countries. For example, France uses a comma for a decimal point. In this case, the starting value of 123 gets converted to 123,00 (correctly) but then changes to 12300 on focus and 12300,00 on blur. Essentially, every focus and blur inadvertently multiplies the value by 100! – peteredhead Sep 03 '20 at 00:34
  • @peteredhead - how to reproduce? Did you change the `currency` variable to something else? – vsync Sep 03 '20 at 18:00
  • The first argument of toLocaleString is the locale to use for formatting. In your code it's set to undefined and therefore uses the browser / system settings. If you want to test how it would work for a user in another country, you could set it to "fr-FR" for example – peteredhead Sep 05 '20 at 00:37
  • Very interesting. Here's a [list of countries](https://en.wikipedia.org/wiki/Decimal_separator) which uses *comma* vs. *decimal point* – vsync Sep 05 '20 at 08:29
  • [I see it is very difficult](https://stackoverflow.com/q/25645163/104380) to convert numbers (in different locales which are represented as *Strings*) back to numbers. There are many different numeral systems. It would require detecting the correct locale and apply a transformation function accordingly. many transformation functions are to be prepared in advance which will bloat the codebase. so sad. – vsync Sep 05 '20 at 08:41
  • @vsync This works great apart from it will only affect the first input set as currency type. If you have multiple inputs set as currency, only the first one will work. I am not knowledgeable enough to understand why. Do you have any insight as to why this could be the case? – Ryan Walkowski Jan 02 '21 at 13:58
  • The code is meant for an ad-hoc demonstration on Stackoverflow, not intended as a complete program which is able do copy-pasted to already-existing code without modifications/re-write. I can guess why it did not work for you. It seems it would be best for your to hire an expect which can integrate my solution for you – vsync Jan 02 '21 at 16:45
  • @vsync I managed to figure it out. I posted a reply on this thread. https://stackoverflow.com/a/65545183/1238706I have credited you with the bulk of the code. It may have been an example but it has worked well for me . Thanks – Ryan Walkowski Jan 02 '21 at 23:35
2

Well in the end I had to compromise by implementing a HTML5/CSS solution, forgoing increment buttons in IE (they're a bit broke in FF anyway!), but gaining number validation that the JQuery spinner doesn't provide. Though I have had to go with a step of whole numbers.

span.gbp {
    float: left;
    text-align: left;
}

span.gbp::before {
    float: left;
    content: "\00a3"; /* £ */
    padding: 3px 4px 3px 3px;
}

span.gbp input {
     width: 280px !important;
}
<label for="broker_fees">Broker Fees</label>
<span class="gbp">
    <input type="number" placeholder="Enter whole GBP (&pound;) or zero for none" min="0" max="10000" step="1" value="" name="Broker_Fees" id="broker_fees" required="required" />
</span>

The validation is a bit flaky across browsers, where IE/FF allow commas and decimal places (as long as it's .00), where as Chrome/Opera don't and want just numbers.

I guess it's a shame that the JQuery spinner won't work with a number type input, but the docs explicitly state not to do that :-( and I'm puzzled as to why a number spinner widget allows input of any ascii char?

Nerdroid
  • 11,584
  • 4
  • 52
  • 62
1DMF
  • 1,625
  • 3
  • 13
  • 16
  • 1
    JSFiddle of above - https://jsfiddle.net/yz9w9g3r Tweak of JSFiddle but with step any and better positioning of currency character - https://jsfiddle.net/yz9w9g3r/1/ – scraymer Sep 04 '15 at 17:55
  • Great, but it should be noted that this isn't a very international solution. (Then again, browsers haven't got i18n for `` worked out properly yet, either: http://stackoverflow.com/questions/15303940/how-to-handle-floats-and-decimal-separators-with-html5-input-type-number) – Michael Scheper Mar 06 '17 at 19:00
1

I stumbled across this article looking for a similar answer. I read @vsync example Using javascript's Number.prototype.toLocaleString: and it appeared to work well. The only complaint I had was that if you had more than a single input type="currency" within your page it would only modify the first instance of it.

As he mentions in his comments it was only designed as an example for stackoverflow.

However, the example worked well for me and although I have little experience with JS I figured out how to modify it so that it will work with multiple input type="currency" on the page using the document.querySelectorAll rather than document.querySelector and adding a for loop.

I hope this can be useful for someone else. ( Credit for the bulk of the code is @vsync )

var currencyInput = document.querySelectorAll( 'input[type="currency"]' );

for ( var i = 0; i < currencyInput.length; i++ ) {

    var currency = 'GBP'
    onBlur( {
        target: currencyInput[ i ]
    } )

    currencyInput[ i ].addEventListener( 'focus', onFocus )
    currencyInput[ i ].addEventListener( 'blur', onBlur )

    function localStringToNumber( s ) {
        return Number( String( s ).replace( /[^0-9.-]+/g, "" ) )
    }

    function onFocus( e ) {
        var value = e.target.value;
        e.target.value = value ? localStringToNumber( value ) : ''
    }

    function onBlur( e ) {
        var value = e.target.value

        var options = {
            maximumFractionDigits: 2,
            currency: currency,
            style: "currency",
            currencyDisplay: "symbol"
        }

        e.target.value = ( value || value === 0 ) ?
            localStringToNumber( value ).toLocaleString( undefined, options ) :
            ''
    }
}

    var currencyInput = document.querySelectorAll( 'input[type="currency"]' );

    for ( var i = 0; i < currencyInput.length; i++ ) {

        var currency = 'GBP'
        onBlur( {
            target: currencyInput[ i ]
        } )

        currencyInput[ i ].addEventListener( 'focus', onFocus )
        currencyInput[ i ].addEventListener( 'blur', onBlur )

        function localStringToNumber( s ) {
            return Number( String( s ).replace( /[^0-9.-]+/g, "" ) )
        }

        function onFocus( e ) {
            var value = e.target.value;
            e.target.value = value ? localStringToNumber( value ) : ''
        }

        function onBlur( e ) {
            var value = e.target.value

            var options = {
                maximumFractionDigits: 2,
                currency: currency,
                style: "currency",
                currencyDisplay: "symbol"
            }

            e.target.value = ( value || value === 0 ) ?
                localStringToNumber( value ).toLocaleString( undefined, options ) :
                ''
        }
    }
.input_date {
    margin:1px 0px 50px 0px;
    font-family: 'Roboto', sans-serif;
    font-size: 18px;
    line-height: 1.5;
    color: #111;
    display: block;
    background: #ddd;
    height: 50px;
    border-radius: 5px;
    border: 2px solid #111111;
    padding: 0 20px 0 20px;
    width: 100px;
}
    <label for="cost_of_sale">Cost of Sale</label>
    <input class="input_date" type="currency" name="cost_of_sale" id="cost_of_sale" value="0.00">

    <label for="sales">Sales</label>
    <input class="input_date" type="currency" name="sales" id="sales" value="0.00">

     <label for="gm_pounds">GM Pounds</label>
     <input class="input_date" type="currency" name="gm_pounds" id="gm_pounds" value="0.00">
Ryan Walkowski
  • 251
  • 8
  • 16
  • 1
    It is also a a very inefficient, wong way of binding events to multiple elements. The proper way has been *event delegation* for about a decade now. – vsync Jan 03 '21 at 08:33
0

We had the same problem for accepting monetary values for Euro, since <input type="number" /> can't display Euro decimal and comma format.

We came up with a solution, to use <input type="number" /> for user input. After user types in the value, we format it and display as a Euro format by just switching to <input type="text" />. This is a Javascript solution though, cuz you need a condition to decide between "user is typing" and "display to user" modes.

Here the link with Visuals to our solution: Input field type "Currency" problem solved

Hope this helps in some way!

nana janashia
  • 101
  • 1
  • 4
0

More easy and beautiful if you has vue.js v-money-spinner :)

enter image description here

user2851148
  • 99
  • 2
  • 6
-3

var currencyInput = document.querySelector('input[type="currency"]')
var currency = 'USD' // https://www.currency-iso.org/dam/downloads/lists/list_one.xml

 // format inital value
onBlur({target:currencyInput})

// bind event listeners
currencyInput.addEventListener('focus', onFocus)
currencyInput.addEventListener('blur', onBlur)


function localStringToNumber( s ){
  return Number(String(s).replace(/[^0-9.-]+/g,""))
}

function onFocus(e){
  var value = e.target.value;
  e.target.value = value ? localStringToNumber(value) : ''
}

function onBlur(e){
  var value = e.target.value

  var options = {
      maximumFractionDigits : 2,
      currency              : currency,
      style                 : "currency",
      currencyDisplay       : "symbol"
  }
  
  e.target.value = value 
    ? localStringToNumber(value).toLocaleString(undefined, options)
    : ''
}
input{
  padding: 10px;
  font: 20px Arial;
  width: 70%;
}
<input type='currency' value="123" placeholder='Type a number & click outside' />