Hi All, I am trying to program a web based cash register/POS application. I would like to use a function to format the number in the "Amount Received" text box.

The idea I have in mind is to always keep two decimal places at all times.
For Example: if someone typed in 2000 it would automatically change that to 20.00

I would probably be triggering the function via onkeyup so it would actually change it on number at a time. 2->.02, 20->.20, 200->2.00, 2000->20.00 ect.

I know I could do it in php when processing the form, but I would really like the number to be already formatted when the form is submitted.

I have been trying to create my own function, but it is nowhere near working so I thought I'd ask if anyone here knows of an existing function or an easy way to accomplish what I am attempting to do.

Any help is appreciated. Thanks in advanced!

Recommended Answers

All 11 Replies

This isn't as easy as you think. The reason is that what the input you are expecting? Is there any prevention from illegal input? How about backspace? Do you enter by copy & paste or each key stroke?

Anyway, the code below handles only invalid char but not backspace, and it modifies value each key stroke. Also, there will always be only 2 decimal and the entering cursor will always be at the end.

<html>
<head>
<script type="text/javascript">
function formatNum(obj) {
  if (obj) {  // object exist
    var val = obj.value
    if (!parseFloat(val) || val.match(/[^\d]$/)) {  // invalid character input
      if (val.length>0) {  // delete invalid char
        obj.value = val.substring(0, val.length-1)
      }
    }
    else {  // valid char input for the key stroke
      if (val.match(/\./)) {  // already added "."
        var idx = val.indexOf(".")
        var front = val.substring(0, idx)  // before "."
        var back = val.substring(idx+1, val.length)  // after "."
        front += back.charAt(0)  // move "." back 1 char
        if (parseInt(front)==0) { front = front.replace(/^0/, "") }  // delete leading "0"
        else { front = front.replace(/^0+/, "") }
        back = back.substring(1, back.length)
        obj.value = front + "." + back
      }
      else {
        obj.value = "0.0"+val
      }
    }
  }
}
</script>
</head>
<body>
<input value = "" onkeyup="formatNum(this)">
</body>

Thanks for your quick and accurate reply. I tried your example and it works just like what I was thinking of. At least good enough.

The only thing I might change/add is if backspace is pressed, clear the whole number as this function doesn't handle backspaces too well. If you have any recommendations on how to add this it would also be appreciated.

I also want to thank you again for your help thus far.

This isn't as easy as you think.

Agreed.
By hitting more-or-less random digits and backspaces I was able to get such display values as
0.2333
23.2
33
instead of the expected
23.33
23.20
33.00

I haven't yet figured out where things go wrong, but I would make the general point that using parseFloat() in what should be an integer operation is as least somewhat risky.

Note: by 'integer' I mean that all of the arithmetic should be done in pennies with the '.' inserted separately.

Also: some consideration should be given to what users expect when they never enter the '.' explicitly; for example, adding machines and calculators have two different modes (one assumes whole dollars and the other doesn't). And there could also be a locale issue for the fraction separator ('.' or ',').

As I stated earlier, this is not an easy case. Also, I have already stated rules that the code could handle (enter cursor must be at the end of string and won't handle backspace). However, the code satisfies your first post requirements. Your original requirement is not detail enough. That's why I asked questions in my previous answer.

If you want to be able to handle backspace as well, you need to use keyboard event handler. The reason is that pure JavaScript function call does not include backspace keystroke. The whole code will be changed, but then there are other issues that you will need to deal with. I am not going to mention anymore about issues that you may encounter because it should be a new thread. Please provide your own code next time and should open a new thread because this one should have been solved under your original requirement.

Below is another code example to deal with backspace, and it should work in both IE and FF. The advantage of using keyboard event is that you force users to enter only valid character, or the script will ignore any invalid chars. The downside is that it is more difficult to maintain if you want any changes.

<html>
<head>
<script type="text/javascript">
function formatNum(evt) {
  // should have attached to add event
  var e = (evt) ? evt : window.event
  var kCode = parseInt(e.keyCode, 10)
  var el = document.getElementById("disp_currency")
  var newChar = ""
  if (kCode>=48 && kCode<=57) {  // number 0~9
    newChar = String.fromCharCode(kCode)
  }
  else if (kCode>=96 && kCode<=105) {  // keypad 0~9
    newChar = String.fromCharCode(kCode-48)
  }
  else if(kCode==8) {  // backspace
    newChar = "X"  // give it a special char
  }

  var frontV, backV, testV, idx
  if (el && newChar.length>0) {  // object exist & valid entry
    var val = el.innerHTML
    var valFloat = parseFloat(val.substring(1, val.length))
    idx = val.indexOf(".")
    frontV = val.substring(0,idx)  // before "."
    backV = val.substring(idx+1,val.length)  // after "."
    if (newChar=="X") {  // user press a backspace
      if (valFloat==0) {  // no value in it, do nothing
      }
      else if (valFloat<0.1) {  // lower than decimal
        el.innerHTML = val.replace(/\d$/,"0")  // replace last digit with 0
      }
      else if (valFloat<1) {  // contain only decimal
        backV = "0" + backV.charAt(0)  // front is 0, work only back
        el.innerHTML = frontV + "." + backV
      }
      else if (valFloat<10) {
        backV = frontV.charAt(frontV.length-1) + backV.charAt(0)
        frontV = frontV.replace(/\d$/,"0")
        el.innerHTML = frontV + "." + backV
      }
      else {
        backV = frontV.charAt(frontV.length-1) + backV.charAt(0)
        frontV = frontV.replace(/\d$/,"")
        el.innerHTML = frontV + "." + backV
      }
    }
    else { // other valid char input for the key stroke
      frontV += backV.charAt(0)  // move "." back 1 char
      testV = frontV.substring(1, frontV.length)
      if (parseInt(testV)==0) { frontV = frontV.replace(/^\$0/, "$") }  // delete leading "0"
      else { frontV = frontV.replace(/^\$0+/, "$") }
      backV = backV.charAt(1) + newChar
      el.innerHTML = frontV + "." + backV
    }
  }
}
window.onload = function() {
  document.onkeyup = formatNum
}
</script>
</head>
<body>
  <div id="disp_currency">$0.00</div>
</body>
</html>

PS: parseFloat() could be used in this case because the precision of decimal is correct up to 7 decimals. This script requires only 2 decimals at most.

this is not an easy case.

Again, agreed.

But may I suggest...

If you keep two hidden fields (call them 'dollars' and 'cents') then handling keystrokes and backspaces is utterly trivial. Add or remove one digit at the insertion point of whichever field is 'active'. Ignore invalid keystrokes (and ignore "0" in 'dollars' when it is null).

Start with 'dollars' active. The way to get to the 'cents' field is to type the '.' key (and the way to leave that field is by pressing backspace enough times).

Displaying the current value is equally trivial: dollars+'.'+cents

No floats, no parses, no regexps, no comparisons, etc., etc.

Two points:
the length of 'cents' is 2 (obviously); if the max length of 'dollars' is 9 or less,
then all operations will be integers.
be careful using keycodes; they are hardware and OS dependent.

Also, I assume that you have checked your logic carefully but using parseInt() in a context in which a leading zero might conceivably occur is dangerous.

Thanks once again for your post. I was going to mark it solved, but I didn't know if that would allow you to post again.

I was thinking of using an event handler as I already am using them to override other keys, like the F* keys, for this project.

To answer your previous questions, since this is for a cash register application the data would be input directly from a keyboard (like your first example is). I don't foresee the use of copy & pasting data into the field, so I don't think that would be a problem. I'll admit, I hadn't thought about verifying that the input characters were only numbers, but I guess it would be the right thing to do. I also hadn't thought of the backspace problem until I was testing the form field and pressed the backspace button and things went haywire.

I didn't mean to sound ungrateful or anything in my previous post, I just hadn't thought of all of the options/possibilities. Again I am sorry for not being as clear as I should have been.

The reason I didn't include any code in my original post is it wasn't anywhere near functional even in the remotest sense of the phrase.

Thanks once again for your examples. Between them and other functions in my existing application I can probably get things to work out all right.

Thanks again for your help.

You are welcome.

@fxm, you are correct about using parseInt. I forgot to use it as I usually do, parseInt(number, base) which would solve the unexpected result. Though, I am not sure that it would be the case here because the leading 0 would not return 0 if there is a number behind. The only bug I know of is that parseInt, without forcing it to base 10, will return a number which is not base 10 if there is leading 0 in the string. In the case above, I use parseInt to check whether or not the value is 0 which would be correct in any base number.

the leading 0 would not return 0 if there is a number behind.

var j = '09'
alert(parseInt(j))

Ah you got me. :P Anyway, use parseInt(j, 10) will give you a 9.

This

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <meta name="generator" content=
    "HTML Tidy for Windows (vers 25 March 2009), see www.w3.org">
    <script type="text/javascript">
 
var dollars = ''
var decimal = '.'
var cents = ''
var pennies = false;
 
function editVal(e) {
    e = e || event
    that = e.target || e.srcElement
 
    switch (e.keyCode) {
	case 8:
            if (pennies == false) {
                if (dollars.length) dollars = dollars.substring(0, dollars.length - 1);
            } else {
                if (cents.length) {
                    cents = cents.substring(0, cents.length - 1)
                } else {
		    pennies = false
                }
            }
	      that.value = dollars + decimal + cents
	      return false
    }
}
 
function showVal(e) {
    e = e || event
    that = e.target || e.srcElement
 
    var m48 = (e.charCode || e.keyCode) - 48
 
    switch (m48) {
        case -2: pennies = true; break;
        case 0: if (!dollars.length && pennies==false) break;
        case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9:
            if (pennies == false) {
                if (dollars.length < 9) dollars = dollars + m48;
            } else {
                if (cents.length < 2) cents = cents + m48;
            }
            break
        default:
    } 
    that.value = dollars + decimal + cents;
    return false;
}
 
    </script>
    <title></title>
  </head>
  <body>
    <form>
      <input id='amount'>
    </form>
    <script type="text/javascript">
	document.getElementById('amount').onkeypress = showVal;
	document.getElementById('amount').onkeydown = editVal;
    </script>
  </body>
</html>

demonstrates the technique (and supports the numeric keypad). If the '.' is pressed it works in pennies; otherwise it works in whole dollars (although it could obviously display '.00' if desired).

AFAIK it works correctly on all browsers/platforms (although the handling of a trailing zero in the 'cents' field needs a little further thought) and I'm fairly sure it behaves in the way a 'naive' user would expect (except for the editing keys, which I probably should disable).

I considered doing an array-based version [using push/pop/slice] but it isn't clear that the code would be significantly simpler.

trailing zero

A trivial change to lines 28 & 51

that.value = dollars + decimal + cents + (cents.length == 1 ? '0' : '');

solves that problem [to my satisfaction, anyway].

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.