Saturday, March 10, 2012

JavaScript: Change entered character in keypress event

Recently I came across a problem with a numeric input field. For globalization issues a decimal had to be separated by a comma instead of a dot. So there were 2 solutions in this case. I could prevent users from typing in the dot, or I could replace the dot by a comma when typed. I went for the second one, because on the numeric keypad you only have a dot. So this way it would be easier for the users to input the decimal. It also works the same as applications like Excel who change the dot to a comma to if your regional settings are set that way.

At first sight this looked like an easy chore. In most cases the keypress or keydown event gets handled and the keyCode of the dot gets blocked. Instead the comma is added at the end of the current value of the input field.

   1: // All decimal input fields have a class named 'number'
   2: $('input.number').each(function (){
   3:     $(this).keypress(function(e){
   4:         // '46' is the keyCode for '.'
   5:         if(e.keyCode == '46' || e.charCode == '46'){ 
   6:             //Cancel the keypress
   7:             e.preventDefault(); 
   8:             // Add the comma to the value of the input field
   9:             $(this).val($this.val() + ',');
  10:         }
  11:     });
  12: });
  13:  

In most cases this will do, but when users start editing the decimal, troubles arrive. When the user types a dot inside the number, the comma will be added at the end of the number. But this isn’t what the user wants. He wants to have the comma placed on the location where he type the dot. After some googling, I found the following solution:



   1: // All decimal input fields have a class named 'number'
   2: $('input.number').each(function () {
   3:     $(this).keypress(function(e){
   4:         // '46' is the keyCode for '.'
   5:         if(e.keyCode == '46' || e.charCode == '46'){
   6:           // IE
   7:           if(document.selection){
   8:                 // Determines the selected text. If no text selected,
   9:                 // the location of the cursor in the text is returned
  10:                 var range = document.selection.createRange();
  11:                 // Place the comma on the location of the selection,
  12:                 // and remove the data in the selection
  13:                 range.text = ',';
  14:           // Chrome + FF
  15:           }else if(this.selectionStart || this.selectionStart == '0'){
  16:                 // Determines the start and end of the selection.
  17:                 // If no text selected, they are the same and
  18:                 // the location of the cursor in the text is returned
  19:                 // Don't make it a jQuery obj, because selectionStart 
  20:                 // and selectionEnd isn't known.
  21:                 var start = this.selectionStart;
  22:                 var end = this.selectionEnd;
  23:                 // Place the comma on the location of the selection,
  24:                 // and remove the data in the selection
  25:                 $(this).val($(this).val().substring(0, start) + ','
  26:                  + $(this).val().substring(end, $(this).val().length));
  27:                 // Set the cursor back at the correct location in 
  28:                 // the text
  29:                 this.selectionStart = start + 1;
  30:                 this.selectionEnd = start +1;
  31:             }else{
  32:                 // if no selection could be determined, 
  33:                 // place the comma at the end.
  34:                 $(this).val($(this).val() + ',');             
  35:             }
  36:             return false;
  37:         }
  38:     });
  39: });



What we do is use the provided functionalities in the browsers for detecting selections in an input field. If no text is selected, the range will return the location of the cursor inside the input field. This way we can provide the correct implementation. So even when text is selected and the dot key is pressed, the selected text will be replaced by the comma.

9 comments:

  1. This is one of my favorite blog because whenever i visit this blog found something interested and different,you are doing very well job,keep it up
    Software Development Company

    ReplyDelete
  2. Thanks a lot for this ;)

    ReplyDelete
  3. Only issue is that one can paste the '.' into the field. Not sure if there's any way to prevent that. is there a function that could prevent from pasting anything at all maybe?

    ReplyDelete
  4. @David: you could for instance disable pasting in the field like this:
    $('input.number').bind('paste', function(event) {
    event.preventDefault();
    });

    Or if you don't want to disable pasting, you could replace the '.' like this:
    $('input.number').bind('paste', function(event) {
    var element = this;
    setTimeout(function() {
    element.value = element.value.replace(/\./g, ',');
    }, 50);
    });

    I don't know any way of doing this without using a timeout however.

    ReplyDelete
  5. how to copy without numbers ?

    ReplyDelete
  6. Was looking just for this. Thanks a lot.

    ReplyDelete
  7. thanks a lot - I was looking for this! =)

    ReplyDelete
  8. Beautiful. Went to about 10 sites with much harder, complex and poor solutions. This does the trick better than anything else i've seen.

    Thank you!

    ReplyDelete